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:
Anthony Liguori 2012-12-13 14:32:28 -06:00
commit e376a788ae
52 changed files with 1349 additions and 449 deletions

View File

@ -215,8 +215,3 @@ void aio_context_unref(AioContext *ctx)
{ {
g_source_unref(&ctx->source); g_source_unref(&ctx->source);
} }
void aio_flush(AioContext *ctx)
{
while (aio_poll(ctx, true));
}

227
block.c
View File

@ -518,22 +518,16 @@ BlockDriver *bdrv_find_protocol(const char *filename)
return NULL; 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; BlockDriver *drv1, *drv;
uint8_t buf[2048]; uint8_t buf[2048];
BlockDriverState *bs; int ret = 0;
ret = bdrv_file_open(&bs, filename, 0);
if (ret < 0) {
*pdrv = NULL;
return ret;
}
/* Return the raw BlockDriver * to scsi-generic devices or empty drives */ /* Return the raw BlockDriver * to scsi-generic devices or empty drives */
if (bs->sg || !bdrv_is_inserted(bs)) { if (bs->sg || !bdrv_is_inserted(bs)) {
bdrv_delete(bs);
drv = bdrv_find_format("raw"); drv = bdrv_find_format("raw");
if (!drv) { if (!drv) {
ret = -ENOENT; ret = -ENOENT;
@ -543,7 +537,6 @@ static int find_image_format(const char *filename, BlockDriver **pdrv)
} }
ret = bdrv_pread(bs, 0, buf, sizeof(buf)); ret = bdrv_pread(bs, 0, buf, sizeof(buf));
bdrv_delete(bs);
if (ret < 0) { if (ret < 0) {
*pdrv = NULL; *pdrv = NULL;
return ret; return ret;
@ -634,10 +627,31 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
bs->copy_on_read--; 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 * 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 flags, BlockDriver *drv)
{ {
int ret, open_flags; 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->opaque = g_malloc0(drv->instance_size);
bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB); bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB);
open_flags = flags | BDRV_O_CACHE_WB; open_flags = bdrv_open_flags(bs, flags);
/*
* 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;
}
bs->read_only = !(open_flags & BDRV_O_RDWR); bs->read_only = !(open_flags & BDRV_O_RDWR);
/* Open the image, either directly or using a protocol */ /* Open the image, either directly or using a protocol */
if (drv->bdrv_file_open) { if (drv->bdrv_file_open) {
ret = drv->bdrv_file_open(bs, filename, open_flags); if (file != NULL) {
} else { bdrv_swap(file, bs);
ret = bdrv_file_open(&bs->file, filename, open_flags); ret = 0;
if (ret >= 0) { } else {
ret = drv->bdrv_open(bs, open_flags); 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) { if (ret < 0) {
@ -709,10 +714,7 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
return 0; return 0;
free_and_fail: free_and_fail:
if (bs->file) { bs->file = NULL;
bdrv_delete(bs->file);
bs->file = NULL;
}
g_free(bs->opaque); g_free(bs->opaque);
bs->opaque = NULL; bs->opaque = NULL;
bs->drv = NULL; bs->drv = NULL;
@ -734,7 +736,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags)
} }
bs = bdrv_new(""); bs = bdrv_new("");
ret = bdrv_open_common(bs, filename, flags, drv); ret = bdrv_open_common(bs, NULL, filename, flags, drv);
if (ret < 0) { if (ret < 0) {
bdrv_delete(bs); bdrv_delete(bs);
return ret; return ret;
@ -789,6 +791,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
int ret; int ret;
/* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
char tmp_filename[PATH_MAX + 1]; char tmp_filename[PATH_MAX + 1];
BlockDriverState *file = NULL;
if (flags & BDRV_O_SNAPSHOT) { if (flags & BDRV_O_SNAPSHOT) {
BlockDriverState *bs1; BlockDriverState *bs1;
@ -848,25 +851,36 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
bs->is_temporary = 1; bs->is_temporary = 1;
} }
/* Find the right image format driver */ /* Open image file without format layer */
if (!drv) {
ret = find_image_format(filename, &drv);
}
if (!drv) {
goto unlink_and_fail;
}
if (flags & BDRV_O_RDWR) { if (flags & BDRV_O_RDWR) {
flags |= BDRV_O_ALLOW_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 */ /* Open the image */
ret = bdrv_open_common(bs, filename, flags, drv); ret = bdrv_open_common(bs, file, filename, flags, drv);
if (ret < 0) { if (ret < 0) {
goto unlink_and_fail; goto unlink_and_fail;
} }
if (bs->file != file) {
bdrv_delete(file);
file = NULL;
}
/* If there is a backing file, use it */ /* If there is a backing file, use it */
if ((flags & BDRV_O_NO_BACKING) == 0) { if ((flags & BDRV_O_NO_BACKING) == 0) {
ret = bdrv_open_backing_file(bs); ret = bdrv_open_backing_file(bs);
@ -888,6 +902,9 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
return 0; return 0;
unlink_and_fail: unlink_and_fail:
if (file != NULL) {
bdrv_delete(file);
}
if (bs->is_temporary) { if (bs->is_temporary) {
unlink(filename); unlink(filename);
} }
@ -3028,7 +3045,46 @@ void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
} }
drv->bdrv_debug_event(bs, 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; BlockDriverAIOCB common;
BlockRequest req; BlockRequest req;
bool is_write; bool is_write;
bool *done;
QEMUBH* bh; QEMUBH* bh;
} BlockDriverAIOCBCoroutine; } BlockDriverAIOCBCoroutine;
static void bdrv_aio_co_cancel_em(BlockDriverAIOCB *blockacb) 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 = { static const AIOCBInfo bdrv_em_co_aiocb_info = {
@ -3796,6 +3860,11 @@ static void bdrv_co_em_bh(void *opaque)
BlockDriverAIOCBCoroutine *acb = opaque; BlockDriverAIOCBCoroutine *acb = opaque;
acb->common.cb(acb->common.opaque, acb->req.error); acb->common.cb(acb->common.opaque, acb->req.error);
if (acb->done) {
*acb->done = true;
}
qemu_bh_delete(acb->bh); qemu_bh_delete(acb->bh);
qemu_aio_release(acb); 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.nb_sectors = nb_sectors;
acb->req.qiov = qiov; acb->req.qiov = qiov;
acb->is_write = is_write; acb->is_write = is_write;
acb->done = NULL;
co = qemu_coroutine_create(bdrv_co_do_rw); co = qemu_coroutine_create(bdrv_co_do_rw);
qemu_coroutine_enter(co, acb); qemu_coroutine_enter(co, acb);
@ -3860,6 +3930,8 @@ BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
BlockDriverAIOCBCoroutine *acb; BlockDriverAIOCBCoroutine *acb;
acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque); acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
acb->done = NULL;
co = qemu_coroutine_create(bdrv_aio_flush_co_entry); co = qemu_coroutine_create(bdrv_aio_flush_co_entry);
qemu_coroutine_enter(co, acb); 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 = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
acb->req.sector = sector_num; acb->req.sector = sector_num;
acb->req.nb_sectors = nb_sectors; acb->req.nb_sectors = nb_sectors;
acb->done = NULL;
co = qemu_coroutine_create(bdrv_aio_discard_co_entry); co = qemu_coroutine_create(bdrv_aio_discard_co_entry);
qemu_coroutine_enter(co, acb); 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; bs->total_time_ns[cookie->type] += get_clock() - cookie->start_time_ns;
} }
int bdrv_img_create(const char *filename, const char *fmt, void bdrv_img_create(const char *filename, const char *fmt,
const char *base_filename, const char *base_fmt, const char *base_filename, const char *base_fmt,
char *options, uint64_t img_size, int flags) char *options, uint64_t img_size, int flags, Error **errp)
{ {
QEMUOptionParameter *param = NULL, *create_options = NULL; QEMUOptionParameter *param = NULL, *create_options = NULL;
QEMUOptionParameter *backing_fmt, *backing_file, *size; 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 */ /* Find driver and parse its options */
drv = bdrv_find_format(fmt); drv = bdrv_find_format(fmt);
if (!drv) { if (!drv) {
error_report("Unknown file format '%s'", fmt); error_setg(errp, "Unknown file format '%s'", fmt);
ret = -EINVAL; return;
goto out;
} }
proto_drv = bdrv_find_protocol(filename); proto_drv = bdrv_find_protocol(filename);
if (!proto_drv) { if (!proto_drv) {
error_report("Unknown protocol '%s'", filename); error_setg(errp, "Unknown protocol '%s'", filename);
ret = -EINVAL; return;
goto out;
} }
create_options = append_option_parameters(create_options, create_options = append_option_parameters(create_options,
@ -4448,8 +4519,7 @@ int bdrv_img_create(const char *filename, const char *fmt,
if (options) { if (options) {
param = parse_option_parameters(options, create_options, param); param = parse_option_parameters(options, create_options, param);
if (param == NULL) { if (param == NULL) {
error_report("Invalid options for file format '%s'.", fmt); error_setg(errp, "Invalid options for file format '%s'.", fmt);
ret = -EINVAL;
goto out; goto out;
} }
} }
@ -4457,18 +4527,16 @@ int bdrv_img_create(const char *filename, const char *fmt,
if (base_filename) { if (base_filename) {
if (set_option_parameter(param, BLOCK_OPT_BACKING_FILE, if (set_option_parameter(param, BLOCK_OPT_BACKING_FILE,
base_filename)) { base_filename)) {
error_report("Backing file not supported for file format '%s'", error_setg(errp, "Backing file not supported for file format '%s'",
fmt); fmt);
ret = -EINVAL;
goto out; goto out;
} }
} }
if (base_fmt) { if (base_fmt) {
if (set_option_parameter(param, BLOCK_OPT_BACKING_FMT, base_fmt)) { if (set_option_parameter(param, BLOCK_OPT_BACKING_FMT, base_fmt)) {
error_report("Backing file format not supported for file " error_setg(errp, "Backing file format not supported for file "
"format '%s'", fmt); "format '%s'", fmt);
ret = -EINVAL;
goto out; 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); backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
if (backing_file && backing_file->value.s) { if (backing_file && backing_file->value.s) {
if (!strcmp(filename, backing_file->value.s)) { if (!strcmp(filename, backing_file->value.s)) {
error_report("Error: Trying to create an image with the " error_setg(errp, "Error: Trying to create an image with the "
"same filename as the backing file"); "same filename as the backing file");
ret = -EINVAL;
goto out; goto out;
} }
} }
@ -4487,9 +4554,8 @@ int bdrv_img_create(const char *filename, const char *fmt,
if (backing_fmt && backing_fmt->value.s) { if (backing_fmt && backing_fmt->value.s) {
backing_drv = bdrv_find_format(backing_fmt->value.s); backing_drv = bdrv_find_format(backing_fmt->value.s);
if (!backing_drv) { if (!backing_drv) {
error_report("Unknown backing file format '%s'", error_setg(errp, "Unknown backing file format '%s'",
backing_fmt->value.s); backing_fmt->value.s);
ret = -EINVAL;
goto out; 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); ret = bdrv_open(bs, backing_file->value.s, back_flags, backing_drv);
if (ret < 0) { 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; goto out;
} }
bdrv_get_geometry(bs, &size); 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); snprintf(buf, sizeof(buf), "%" PRId64, size);
set_option_parameter(param, BLOCK_OPT_SIZE, buf); set_option_parameter(param, BLOCK_OPT_SIZE, buf);
} else { } else {
error_report("Image creation needs a size parameter"); error_setg(errp, "Image creation needs a size parameter");
ret = -EINVAL;
goto out; goto out;
} }
} }
@ -4531,17 +4597,16 @@ int bdrv_img_create(const char *filename, const char *fmt,
puts(""); puts("");
ret = bdrv_create(drv, filename, param); ret = bdrv_create(drv, filename, param);
if (ret < 0) { if (ret < 0) {
if (ret == -ENOTSUP) { if (ret == -ENOTSUP) {
error_report("Formatting or formatting option not supported for " error_setg(errp,"Formatting or formatting option not supported for "
"file format '%s'", fmt); "file format '%s'", fmt);
} else if (ret == -EFBIG) { } else if (ret == -EFBIG) {
error_report("The image size is too large for file format '%s'", error_setg(errp, "The image size is too large for file format '%s'",
fmt); fmt);
} else { } else {
error_report("%s: error while creating %s: %s", filename, fmt, error_setg(errp, "%s: error while creating %s: %s", filename, fmt,
strerror(-ret)); strerror(-ret));
} }
} }
@ -4552,6 +4617,4 @@ out:
if (bs) { if (bs) {
bdrv_delete(bs); bdrv_delete(bs);
} }
return ret;
} }

11
block.h
View File

@ -343,9 +343,9 @@ int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
int64_t pos, int size); int64_t pos, int size);
int bdrv_img_create(const char *filename, const char *fmt, void bdrv_img_create(const char *filename, const char *fmt,
const char *base_filename, const char *base_fmt, const char *base_filename, const char *base_fmt,
char *options, uint64_t img_size, int flags); char *options, uint64_t img_size, int flags, Error **errp);
void bdrv_set_buffer_alignment(BlockDriverState *bs, int align); void bdrv_set_buffer_alignment(BlockDriverState *bs, int align);
void *qemu_blockalign(BlockDriverState *bs, size_t size); void *qemu_blockalign(BlockDriverState *bs, size_t size);
@ -431,4 +431,9 @@ typedef enum {
#define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt) #define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt)
void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event); 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 #endif

View File

@ -29,8 +29,10 @@
typedef struct BDRVBlkdebugState { typedef struct BDRVBlkdebugState {
int state; int state;
int new_state; int new_state;
QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX]; QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
QSIMPLEQ_HEAD(, BlkdebugRule) active_rules; QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
} BDRVBlkdebugState; } BDRVBlkdebugState;
typedef struct BlkdebugAIOCB { typedef struct BlkdebugAIOCB {
@ -39,6 +41,12 @@ typedef struct BlkdebugAIOCB {
int ret; int ret;
} BlkdebugAIOCB; } BlkdebugAIOCB;
typedef struct BlkdebugSuspendedReq {
Coroutine *co;
char *tag;
QLIST_ENTRY(BlkdebugSuspendedReq) next;
} BlkdebugSuspendedReq;
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb); static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb);
static const AIOCBInfo blkdebug_aiocb_info = { static const AIOCBInfo blkdebug_aiocb_info = {
@ -49,6 +57,7 @@ static const AIOCBInfo blkdebug_aiocb_info = {
enum { enum {
ACTION_INJECT_ERROR, ACTION_INJECT_ERROR,
ACTION_SET_STATE, ACTION_SET_STATE,
ACTION_SUSPEND,
}; };
typedef struct BlkdebugRule { typedef struct BlkdebugRule {
@ -65,6 +74,9 @@ typedef struct BlkdebugRule {
struct { struct {
int new_state; int new_state;
} set_state; } set_state;
struct {
char *tag;
} suspend;
} options; } options;
QLIST_ENTRY(BlkdebugRule) next; QLIST_ENTRY(BlkdebugRule) next;
QSIMPLEQ_ENTRY(BlkdebugRule) active_next; QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
@ -226,6 +238,11 @@ static int add_rule(QemuOpts *opts, void *opaque)
rule->options.set_state.new_state = rule->options.set_state.new_state =
qemu_opt_get_number(opts, "new_state", 0); qemu_opt_get_number(opts, "new_state", 0);
break; break;
case ACTION_SUSPEND:
rule->options.suspend.tag =
g_strdup(qemu_opt_get(opts, "tag"));
break;
}; };
/* Add the rule */ /* Add the rule */
@ -234,12 +251,32 @@ static int add_rule(QemuOpts *opts, void *opaque)
return 0; 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) static int read_config(BDRVBlkdebugState *s, const char *filename)
{ {
FILE *f; FILE *f;
int ret; int ret;
struct add_rule_data d; struct add_rule_data d;
/* Allow usage without config file */
if (!*filename) {
return 0;
}
f = fopen(filename, "r"); f = fopen(filename, "r");
if (f == NULL) { if (f == NULL) {
return -errno; 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); return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
} }
static void blkdebug_close(BlockDriverState *bs) static void blkdebug_close(BlockDriverState *bs)
{ {
BDRVBlkdebugState *s = bs->opaque; BDRVBlkdebugState *s = bs->opaque;
@ -397,12 +435,32 @@ static void blkdebug_close(BlockDriverState *bs)
for (i = 0; i < BLKDBG_EVENT_MAX; i++) { for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) { QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
QLIST_REMOVE(rule, next); remove_rule(rule);
g_free(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, static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
bool injected) bool injected)
{ {
@ -426,6 +484,10 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
case ACTION_SET_STATE: case ACTION_SET_STATE:
s->new_state = rule->options.set_state.new_state; s->new_state = rule->options.set_state.new_state;
break; break;
case ACTION_SUSPEND:
suspend_request(bs, rule);
break;
} }
return injected; return injected;
} }
@ -433,19 +495,72 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event) static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
{ {
BDRVBlkdebugState *s = bs->opaque; BDRVBlkdebugState *s = bs->opaque;
struct BlkdebugRule *rule; struct BlkdebugRule *rule, *next;
bool injected; bool injected;
assert((int)event >= 0 && event < BLKDBG_EVENT_MAX); assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
injected = false; injected = false;
s->new_state = s->state; 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); injected = process_rule(bs, rule, injected);
} }
s->state = s->new_state; 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) static int64_t blkdebug_getlength(BlockDriverState *bs)
{ {
return bdrv_getlength(bs->file); return bdrv_getlength(bs->file);
@ -464,7 +579,10 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_aio_readv = blkdebug_aio_readv, .bdrv_aio_readv = blkdebug_aio_readv,
.bdrv_aio_writev = blkdebug_aio_writev, .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) static void bdrv_blkdebug_init(void)

View File

@ -103,7 +103,7 @@ static void coroutine_fn commit_run(void *opaque)
wait: wait:
/* Note that even when no rate limit is applied we need to yield /* 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); block_job_sleep_ns(&s->common, rt_clock, delay_ns);
if (block_job_is_cancelled(&s->common)) { if (block_job_is_cancelled(&s->common)) {

View File

@ -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 /* 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); block_job_sleep_ns(&s->common, rt_clock, delay_ns);
if (block_job_is_cancelled(&s->common)) { if (block_job_is_cancelled(&s->common)) {

View File

@ -615,57 +615,67 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
return cluster_offset; 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; BDRVQcowState *s = bs->opaque;
int i, j = 0, l2_index, ret; int ret;
uint64_t *old_cluster, start_sect, *l2_table;
uint64_t cluster_offset = m->alloc_offset;
bool cow = false;
trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters); if (r->nb_sectors == 0) {
if (m->nb_clusters == 0)
return 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)) { qemu_co_mutex_unlock(&s->lock);
cow = true; ret = copy_sectors(bs, m->offset / BDRV_SECTOR_SIZE, m->alloc_offset,
qemu_co_mutex_unlock(&s->lock); r->offset / BDRV_SECTOR_SIZE,
ret = copy_sectors(bs, start_sect, cluster_offset, m->nb_available, r->offset / BDRV_SECTOR_SIZE + r->nb_sectors);
align_offset(m->nb_available, s->cluster_sectors)); qemu_co_mutex_lock(&s->lock);
qemu_co_mutex_lock(&s->lock);
if (ret < 0) if (ret < 0) {
goto err; return ret;
} }
/* /*
* Update L2 table.
*
* Before we update the L2 table to actually point to the new cluster, we * 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 * need to be sure that the refcounts have been increased and COW was
* handled. * 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)) { if (qcow2_need_accurate_refcounts(s)) {
qcow2_cache_set_dependency(bs, s->l2_table_cache, qcow2_cache_set_dependency(bs, s->l2_table_cache,
s->refcount_block_cache); s->refcount_block_cache);
} }
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index); ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index);
if (ret < 0) { if (ret < 0) {
goto err; goto err;
@ -743,38 +753,16 @@ out:
} }
/* /*
* Allocates new clusters for the given guest_offset. * 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
* At most *nb_clusters are allocated, and on return *nb_clusters is updated to * request has completed and updated the L2 table accordingly.
* 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, static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset,
uint64_t *host_offset, unsigned int *nb_clusters) unsigned int *nb_clusters)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
QCowL2Meta *old_alloc; 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) { QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) {
uint64_t start = guest_offset >> s->cluster_bits; 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(); 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 */ /* Allocate new clusters */
trace_qcow2_cluster_alloc_phys(qemu_coroutine_self()); trace_qcow2_cluster_alloc_phys(qemu_coroutine_self());
if (*host_offset == 0) { if (*host_offset == 0) {
@ -818,7 +842,7 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
*host_offset = cluster_offset; *host_offset = cluster_offset;
return 0; return 0;
} else { } 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) { if (ret < 0) {
return ret; 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 * Return 0 on success and -errno in error cases
*/ */
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t 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)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
int l2_index, ret, sectors; int l2_index, ret, sectors;
@ -919,12 +943,6 @@ again:
} }
/* If there is something left to allocate, do that now */ /* 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) { if (nb_clusters > 0) {
uint64_t alloc_offset; uint64_t alloc_offset;
uint64_t alloc_cluster_offset; uint64_t alloc_cluster_offset;
@ -957,22 +975,40 @@ again:
* *
* avail_sectors: Number of sectors from the start of the first * avail_sectors: Number of sectors from the start of the first
* newly allocated to the end of the last newly allocated cluster. * 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 requested_sectors = n_end - keep_clusters * s->cluster_sectors;
int avail_sectors = nb_clusters int avail_sectors = nb_clusters
<< (s->cluster_bits - BDRV_SECTOR_BITS); << (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) { if (keep_clusters == 0) {
.cluster_offset = keep_clusters == 0 ? cluster_offset = alloc_cluster_offset;
alloc_cluster_offset : cluster_offset, }
*m = g_malloc0(sizeof(**m));
**m = (QCowL2Meta) {
.alloc_offset = alloc_cluster_offset, .alloc_offset = alloc_cluster_offset,
.offset = alloc_offset, .offset = alloc_offset & ~(s->cluster_size - 1),
.n_start = keep_clusters == 0 ? n_start : 0,
.nb_clusters = nb_clusters, .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); qemu_co_queue_init(&(*m)->dependent_requests);
QLIST_INSERT_HEAD(&s->cluster_allocs, m, next_in_flight); QLIST_INSERT_HEAD(&s->cluster_allocs, *m, next_in_flight);
} }
} }
@ -984,12 +1020,13 @@ again:
assert(sectors > n_start); assert(sectors > n_start);
*num = sectors - n_start; *num = sectors - n_start;
*host_offset = cluster_offset;
return 0; return 0;
fail: fail:
if (m->nb_clusters > 0) { if (*m && (*m)->nb_clusters > 0) {
QLIST_REMOVE(m, next_in_flight); QLIST_REMOVE(*m, next_in_flight);
} }
return ret; return ret;
} }

View File

@ -222,7 +222,7 @@ static void report_unsupported_feature(BlockDriverState *bs,
* updated successfully. Therefore it is not required to check the return * updated successfully. Therefore it is not required to check the return
* value of this function. * value of this function.
*/ */
static int qcow2_mark_dirty(BlockDriverState *bs) int qcow2_mark_dirty(BlockDriverState *bs)
{ {
BDRVQcowState *s = bs->opaque; BDRVQcowState *s = bs->opaque;
uint64_t val; uint64_t val;
@ -745,21 +745,6 @@ fail:
return ret; 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, static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
int64_t sector_num, int64_t sector_num,
int remaining_sectors, int remaining_sectors,
@ -774,15 +759,11 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
QEMUIOVector hd_qiov; QEMUIOVector hd_qiov;
uint64_t bytes_done = 0; uint64_t bytes_done = 0;
uint8_t *cluster_data = NULL; uint8_t *cluster_data = NULL;
QCowL2Meta l2meta = { QCowL2Meta *l2meta;
.nb_clusters = 0,
};
trace_qcow2_writev_start_req(qemu_coroutine_self(), sector_num, trace_qcow2_writev_start_req(qemu_coroutine_self(), sector_num,
remaining_sectors); remaining_sectors);
qemu_co_queue_init(&l2meta.dependent_requests);
qemu_iovec_init(&hd_qiov, qiov->niov); qemu_iovec_init(&hd_qiov, qiov->niov);
s->cluster_cache_offset = -1; /* disable compressed cache */ 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) { while (remaining_sectors != 0) {
l2meta = NULL;
trace_qcow2_writev_start_part(qemu_coroutine_self()); trace_qcow2_writev_start_part(qemu_coroutine_self());
index_in_cluster = sector_num & (s->cluster_sectors - 1); index_in_cluster = sector_num & (s->cluster_sectors - 1);
n_end = index_in_cluster + remaining_sectors; 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, 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) { if (ret < 0) {
goto fail; 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); assert((cluster_offset & 511) == 0);
qemu_iovec_reset(&hd_qiov); qemu_iovec_reset(&hd_qiov);
@ -835,8 +812,8 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
cur_nr_sectors * 512); cur_nr_sectors * 512);
} }
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
trace_qcow2_writev_data(qemu_coroutine_self(), trace_qcow2_writev_data(qemu_coroutine_self(),
(cluster_offset >> 9) + index_in_cluster); (cluster_offset >> 9) + index_in_cluster);
ret = bdrv_co_writev(bs->file, ret = bdrv_co_writev(bs->file,
@ -847,12 +824,24 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
goto fail; goto fail;
} }
ret = qcow2_alloc_cluster_link_l2(bs, &l2meta); if (l2meta != NULL) {
if (ret < 0) { ret = qcow2_alloc_cluster_link_l2(bs, l2meta);
goto fail; 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; remaining_sectors -= cur_nr_sectors;
sector_num += cur_nr_sectors; sector_num += cur_nr_sectors;
@ -862,10 +851,16 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
ret = 0; ret = 0;
fail: fail:
run_dependent_requests(s, &l2meta);
qemu_co_mutex_unlock(&s->lock); 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_iovec_destroy(&hd_qiov);
qemu_vfree(cluster_data); qemu_vfree(cluster_data);
trace_qcow2_writev_done_req(qemu_coroutine_self(), ret); trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
@ -1128,31 +1123,33 @@ static int preallocate(BlockDriverState *bs)
{ {
uint64_t nb_sectors; uint64_t nb_sectors;
uint64_t offset; uint64_t offset;
uint64_t host_offset = 0;
int num; int num;
int ret; int ret;
QCowL2Meta meta; QCowL2Meta *meta;
nb_sectors = bdrv_getlength(bs) >> 9; nb_sectors = bdrv_getlength(bs) >> 9;
offset = 0; offset = 0;
qemu_co_queue_init(&meta.dependent_requests);
meta.cluster_offset = 0;
while (nb_sectors) { while (nb_sectors) {
num = MIN(nb_sectors, INT_MAX >> 9); 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) { if (ret < 0) {
return ret; return ret;
} }
ret = qcow2_alloc_cluster_link_l2(bs, &meta); ret = qcow2_alloc_cluster_link_l2(bs, meta);
if (ret < 0) { 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; return ret;
} }
/* There are no dependent requests, but we need to remove our request /* There are no dependent requests, but we need to remove our request
* from the list of in-flight requests */ * 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 */ /* 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 * all of the allocated clusters (otherwise we get failing reads after
* EOF). Extend the image to the last allocated sector. * EOF). Extend the image to the last allocated sector.
*/ */
if (meta.cluster_offset != 0) { if (host_offset != 0) {
uint8_t buf[512]; uint8_t buf[512];
memset(buf, 0, 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) { if (ret < 0) {
return ret; return ret;
} }

View File

@ -196,17 +196,56 @@ typedef struct QCowCreateState {
struct QCowAIOCB; 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 typedef struct QCowL2Meta
{ {
/** Guest offset of the first newly allocated cluster */
uint64_t offset; uint64_t offset;
uint64_t cluster_offset;
/** Host offset of the first newly allocated cluster */
uint64_t alloc_offset; 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; int nb_available;
/** Number of newly allocated clusters */
int nb_clusters; int nb_clusters;
/**
* Requests that overlap with this allocation and wait to be restarted
* when the allocating request has completed.
*/
CoQueue dependent_requests; 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; QLIST_ENTRY(QCowL2Meta) next_in_flight;
} QCowL2Meta; } QCowL2Meta;
@ -264,6 +303,8 @@ static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s)
/* qcow2.c functions */ /* qcow2.c functions */
int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t sector_num, int nb_sectors); int64_t sector_num, int nb_sectors);
int qcow2_mark_dirty(BlockDriverState *bs);
int qcow2_update_header(BlockDriverState *bs); int qcow2_update_header(BlockDriverState *bs);
/* qcow2-refcount.c functions */ /* 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 qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
int *num, uint64_t *cluster_offset); int *num, uint64_t *cluster_offset);
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t 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 qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
uint64_t offset, uint64_t offset,
int compressed_size); int compressed_size);

View File

@ -708,22 +708,6 @@ static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
return thread_pool_submit_aio(aio_worker, acb, cb, opaque); 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, static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockDriverCompletionFunc *cb, void *opaque, int type) BlockDriverCompletionFunc *cb, void *opaque, int type)
@ -1346,10 +1330,19 @@ static BlockDriverAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
BlockDriverCompletionFunc *cb, void *opaque) BlockDriverCompletionFunc *cb, void *opaque)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
RawPosixAIOData *acb;
if (fd_open(bs) < 0) if (fd_open(bs) < 0)
return NULL; 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__) #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)

View File

@ -303,13 +303,24 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
LONG low, high; LONG low, high;
DWORD dwPtrLow;
low = offset; low = offset;
high = offset >> 32; 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; return -EIO;
}
if (SetEndOfFile(s->hfile) == 0) {
fprintf(stderr, "SetEndOfFile error: %d\n", GetLastError());
return -EIO;
}
return 0; return 0;
} }

View File

@ -77,6 +77,7 @@ typedef struct RBDAIOCB {
int error; int error;
struct BDRVRBDState *s; struct BDRVRBDState *s;
int cancelled; int cancelled;
int status;
} RBDAIOCB; } RBDAIOCB;
typedef struct RADOSCB { typedef struct RADOSCB {
@ -376,12 +377,6 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
RBDAIOCB *acb = rcb->acb; RBDAIOCB *acb = rcb->acb;
int64_t r; int64_t r;
if (acb->cancelled) {
qemu_vfree(acb->bounce);
qemu_aio_release(acb);
goto done;
}
r = rcb->ret; r = rcb->ret;
if (acb->cmd == RBD_AIO_WRITE || 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 */ /* Note that acb->bh can be NULL in case where the aio was cancelled */
acb->bh = qemu_bh_new(rbd_aio_bh_cb, acb); acb->bh = qemu_bh_new(rbd_aio_bh_cb, acb);
qemu_bh_schedule(acb->bh); qemu_bh_schedule(acb->bh);
done:
g_free(rcb); g_free(rcb);
} }
@ -568,6 +562,12 @@ static void qemu_rbd_aio_cancel(BlockDriverAIOCB *blockacb)
{ {
RBDAIOCB *acb = (RBDAIOCB *) blockacb; RBDAIOCB *acb = (RBDAIOCB *) blockacb;
acb->cancelled = 1; acb->cancelled = 1;
while (acb->status == -EINPROGRESS) {
qemu_aio_wait();
}
qemu_aio_release(acb);
} }
static const AIOCBInfo rbd_aiocb_info = { 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)); acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
qemu_bh_delete(acb->bh); qemu_bh_delete(acb->bh);
acb->bh = NULL; 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, 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->s = s;
acb->cancelled = 0; acb->cancelled = 0;
acb->bh = NULL; acb->bh = NULL;
acb->status = -EINPROGRESS;
if (cmd == RBD_AIO_WRITE) { if (cmd == RBD_AIO_WRITE) {
qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size); qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size);

View File

@ -108,7 +108,7 @@ static void coroutine_fn stream_run(void *opaque)
wait: wait:
/* Note that even when no rate limit is applied we need to yield /* 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); block_job_sleep_ns(&s->common, rt_clock, delay_ns);
if (block_job_is_cancelled(&s->common)) { if (block_job_is_cancelled(&s->common)) {

View File

@ -26,6 +26,9 @@
#include "block_int.h" #include "block_int.h"
#include "module.h" #include "module.h"
#include "migration.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) bs->total_sectors = (int64_t)
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl; 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; err = -EFBIG;
goto fail; 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 * Note that the geometry doesn't always exactly match total_sectors but
* may round it down. * 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, static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
uint8_t* heads, uint8_t* secs_per_cyl) uint8_t* heads, uint8_t* secs_per_cyl)
{ {
uint32_t cyls_times_heads; 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; return -EFBIG;
}
if (total_sectors > 65535 * 16 * 63) { if (total_sectors > 65535 * 16 * 63) {
*secs_per_cyl = 255; *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; cyls_times_heads = total_sectors / *secs_per_cyl;
} else { } else {
*secs_per_cyl = 17; *secs_per_cyl = 17;
@ -739,7 +751,9 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
footer->type = be32_to_cpu(disk_type); 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)); footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));

View File

@ -190,6 +190,12 @@ struct BlockDriver {
void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event); 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 * Returns 1 if newly created images are guaranteed to contain only
* zeros, 0 otherwise. * zeros, 0 otherwise.

View File

@ -275,7 +275,7 @@ static bool do_check_io_limits(BlockIOLimit *io_limits)
return true; 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 *buf;
const char *file = NULL; const char *file = NULL;
@ -325,7 +325,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
return NULL; return NULL;
} }
} else { } else {
type = default_to_scsi ? IF_SCSI : IF_IDE; type = block_default_type;
} }
max_devs = if_max_devs[type]; max_devs = if_max_devs[type];
@ -568,7 +568,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
break; break;
case IF_VIRTIO: case IF_VIRTIO:
/* add virtio block device */ /* 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) { if (arch_type == QEMU_ARCH_S390X) {
qemu_opt_set(opts, "driver", "virtio-blk-s390"); qemu_opt_set(opts, "driver", "virtio-blk-s390");
} else { } else {
@ -707,6 +707,7 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
int ret = 0; int ret = 0;
BlockdevActionList *dev_entry = dev_list; BlockdevActionList *dev_entry = dev_list;
BlkTransactionStates *states, *next; BlkTransactionStates *states, *next;
Error *local_err = NULL;
QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionStates) snap_bdrv_states; QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionStates) snap_bdrv_states;
QSIMPLEQ_INIT(&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 */ /* create new image w/backing file */
if (mode != NEW_IMAGE_MODE_EXISTING) { if (mode != NEW_IMAGE_MODE_EXISTING) {
ret = bdrv_img_create(new_image_file, format, bdrv_img_create(new_image_file, format,
states->old_bs->filename, states->old_bs->filename,
states->old_bs->drv->format_name, states->old_bs->drv->format_name,
NULL, -1, flags); NULL, -1, flags, &local_err);
if (ret) { if (error_is_set(&local_err)) {
error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file); error_propagate(errp, local_err);
goto delete_and_fail; goto delete_and_fail;
} }
} }
@ -1263,8 +1264,8 @@ void qmp_drive_mirror(const char *device, const char *target,
assert(format && drv); assert(format && drv);
bdrv_get_geometry(bs, &size); bdrv_get_geometry(bs, &size);
size *= 512; size *= 512;
ret = bdrv_img_create(target, format, bdrv_img_create(target, format,
NULL, NULL, NULL, size, flags); NULL, NULL, NULL, size, flags, &local_err);
} else { } else {
switch (mode) { switch (mode) {
case NEW_IMAGE_MODE_EXISTING: case NEW_IMAGE_MODE_EXISTING:
@ -1272,18 +1273,18 @@ void qmp_drive_mirror(const char *device, const char *target,
break; break;
case NEW_IMAGE_MODE_ABSOLUTE_PATHS: case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
/* create new image with backing file */ /* create new image with backing file */
ret = bdrv_img_create(target, format, bdrv_img_create(target, format,
source->filename, source->filename,
source->drv->format_name, source->drv->format_name,
NULL, -1, flags); NULL, -1, flags, &local_err);
break; break;
default: default:
abort(); abort();
} }
} }
if (ret) { if (error_is_set(&local_err)) {
error_set(errp, QERR_OPEN_FILE_FAILED, target); error_propagate(errp, local_err);
return; return;
} }

View File

@ -19,8 +19,13 @@ void blockdev_auto_del(BlockDriverState *bs);
typedef enum { typedef enum {
IF_DEFAULT = -1, /* for use with drive_add() only */ 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_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 IF_COUNT
} BlockInterfaceType; } BlockInterfaceType;
@ -51,7 +56,7 @@ DriveInfo *drive_get_by_blockdev(BlockDriverState *bs);
QemuOpts *drive_def(const char *optstr); QemuOpts *drive_def(const char *optstr);
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
const char *optstr); const char *optstr);
DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi); DriveInfo *drive_init(QemuOpts *arg, BlockInterfaceType block_default_type);
/* device-hotplug */ /* device-hotplug */

View File

@ -3,6 +3,7 @@
#ifndef HW_BOARDS_H #ifndef HW_BOARDS_H
#define HW_BOARDS_H #define HW_BOARDS_H
#include "blockdev.h"
#include "qdev.h" #include "qdev.h"
typedef struct QEMUMachineInitArgs { typedef struct QEMUMachineInitArgs {
@ -24,7 +25,7 @@ typedef struct QEMUMachine {
const char *desc; const char *desc;
QEMUMachineInitFunc *init; QEMUMachineInitFunc *init;
QEMUMachineResetFunc *reset; QEMUMachineResetFunc *reset;
int use_scsi; BlockInterfaceType block_default_type;
int max_cpus; int max_cpus;
unsigned int no_serial:1, unsigned int no_serial:1,
no_parallel:1, no_parallel:1,

View File

@ -39,7 +39,7 @@ DriveInfo *add_init_drive(const char *optstr)
if (!opts) if (!opts)
return NULL; return NULL;
dinfo = drive_init(opts, current_machine->use_scsi); dinfo = drive_init(opts, current_machine->block_default_type);
if (!dinfo) { if (!dinfo) {
qemu_opts_del(opts); qemu_opts_del(opts);
return NULL; return NULL;

View File

@ -329,7 +329,7 @@ static QEMUMachine highbank_machine = {
.name = "highbank", .name = "highbank",
.desc = "Calxeda Highbank (ECX-1000)", .desc = "Calxeda Highbank (ECX-1000)",
.init = highbank_init, .init = highbank_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
.max_cpus = 4, .max_cpus = 4,
}; };

View File

@ -1869,6 +1869,8 @@ static void ide_reset(IDEState *s)
s->io_buffer_index = 0; s->io_buffer_index = 0;
s->cd_sector_size = 0; s->cd_sector_size = 0;
s->atapi_dma = 0; s->atapi_dma = 0;
s->tray_locked = 0;
s->tray_open = 0;
/* ATA DMA state */ /* ATA DMA state */
s->io_buffer_size = 0; s->io_buffer_size = 0;
s->req_nb_sectors = 0; s->req_nb_sectors = 0;

View File

@ -212,7 +212,6 @@ static QEMUMachine leon3_generic_machine = {
.name = "leon3_generic", .name = "leon3_generic",
.desc = "Leon-3 generic", .desc = "Leon-3 generic",
.init = leon3_generic_hw_init, .init = leon3_generic_hw_init,
.use_scsi = 0,
}; };
static void leon3_machine_init(void) static void leon3_machine_init(void)

View File

@ -324,14 +324,14 @@ static QEMUMachine mips_magnum_machine = {
.name = "magnum", .name = "magnum",
.desc = "MIPS Magnum", .desc = "MIPS Magnum",
.init = mips_magnum_init, .init = mips_magnum_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
}; };
static QEMUMachine mips_pica61_machine = { static QEMUMachine mips_pica61_machine = {
.name = "pica61", .name = "pica61",
.desc = "Acer Pica 61", .desc = "Acer Pica 61",
.init = mips_pica61_init, .init = mips_pica61_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
}; };
static void mips_jazz_machine_init(void) static void mips_jazz_machine_init(void)

View File

@ -98,7 +98,7 @@ static void pc_fw_add_pflash_drv(void)
return; return;
} }
if (!drive_init(opts, machine->use_scsi)) { if (!drive_init(opts, machine->block_default_type)) {
qemu_opts_del(opts); qemu_opts_del(opts);
} }
} }

View File

@ -122,7 +122,6 @@ static QEMUMachine puv3_machine = {
.desc = "PKUnity Version-3 based on UniCore32", .desc = "PKUnity Version-3 based on UniCore32",
.init = puv3_init, .init = puv3_init,
.is_default = 1, .is_default = 1,
.use_scsi = 0,
}; };
static void puv3_machine_init(void) static void puv3_machine_init(void)

View File

@ -364,14 +364,14 @@ static QEMUMachine realview_eb_machine = {
.name = "realview-eb", .name = "realview-eb",
.desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)", .desc = "ARM RealView Emulation Baseboard (ARM926EJ-S)",
.init = realview_eb_init, .init = realview_eb_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
}; };
static QEMUMachine realview_eb_mpcore_machine = { static QEMUMachine realview_eb_mpcore_machine = {
.name = "realview-eb-mpcore", .name = "realview-eb-mpcore",
.desc = "ARM RealView Emulation Baseboard (ARM11MPCore)", .desc = "ARM RealView Emulation Baseboard (ARM11MPCore)",
.init = realview_eb_mpcore_init, .init = realview_eb_mpcore_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
.max_cpus = 4, .max_cpus = 4,
}; };
@ -385,7 +385,7 @@ static QEMUMachine realview_pbx_a9_machine = {
.name = "realview-pbx-a9", .name = "realview-pbx-a9",
.desc = "ARM RealView Platform Baseboard Explore for Cortex-A9", .desc = "ARM RealView Platform Baseboard Explore for Cortex-A9",
.init = realview_pbx_a9_init, .init = realview_pbx_a9_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
.max_cpus = 4, .max_cpus = 4,
}; };

View File

@ -314,21 +314,6 @@ static void s390_init(QEMUMachineInitArgs *args)
qdev_set_nic_properties(dev, nd); qdev_set_nic_properties(dev, nd);
qdev_init_nofail(dev); 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 = { static QEMUMachine s390_machine = {
@ -336,6 +321,7 @@ static QEMUMachine s390_machine = {
.alias = "s390", .alias = "s390",
.desc = "VirtIO based S390 machine", .desc = "VirtIO based S390 machine",
.init = s390_init, .init = s390_init,
.block_default_type = IF_VIRTIO,
.no_cdrom = 1, .no_cdrom = 1,
.no_floppy = 1, .no_floppy = 1,
.no_serial = 1, .no_serial = 1,
@ -352,3 +338,4 @@ static void s390_machine_init(void)
} }
machine_init(s390_machine_init); machine_init(s390_machine_init);

View File

@ -924,9 +924,9 @@ static QEMUMachine spapr_machine = {
.desc = "pSeries Logical Partition (PAPR compliant)", .desc = "pSeries Logical Partition (PAPR compliant)",
.init = ppc_spapr_init, .init = ppc_spapr_init,
.reset = ppc_spapr_reset, .reset = ppc_spapr_reset,
.block_default_type = IF_SCSI,
.max_cpus = MAX_CPUS, .max_cpus = MAX_CPUS,
.no_parallel = 1, .no_parallel = 1,
.use_scsi = 1,
}; };
static void spapr_machine_init(void) static void spapr_machine_init(void)

View File

@ -1426,7 +1426,7 @@ static QEMUMachine ss5_machine = {
.name = "SS-5", .name = "SS-5",
.desc = "Sun4m platform, SPARCstation 5", .desc = "Sun4m platform, SPARCstation 5",
.init = ss5_init, .init = ss5_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
.is_default = 1, .is_default = 1,
}; };
@ -1434,7 +1434,7 @@ static QEMUMachine ss10_machine = {
.name = "SS-10", .name = "SS-10",
.desc = "Sun4m platform, SPARCstation 10", .desc = "Sun4m platform, SPARCstation 10",
.init = ss10_init, .init = ss10_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
.max_cpus = 4, .max_cpus = 4,
}; };
@ -1442,7 +1442,7 @@ static QEMUMachine ss600mp_machine = {
.name = "SS-600MP", .name = "SS-600MP",
.desc = "Sun4m platform, SPARCserver 600MP", .desc = "Sun4m platform, SPARCserver 600MP",
.init = ss600mp_init, .init = ss600mp_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
.max_cpus = 4, .max_cpus = 4,
}; };
@ -1450,7 +1450,7 @@ static QEMUMachine ss20_machine = {
.name = "SS-20", .name = "SS-20",
.desc = "Sun4m platform, SPARCstation 20", .desc = "Sun4m platform, SPARCstation 20",
.init = ss20_init, .init = ss20_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
.max_cpus = 4, .max_cpus = 4,
}; };
@ -1458,35 +1458,35 @@ static QEMUMachine voyager_machine = {
.name = "Voyager", .name = "Voyager",
.desc = "Sun4m platform, SPARCstation Voyager", .desc = "Sun4m platform, SPARCstation Voyager",
.init = vger_init, .init = vger_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
}; };
static QEMUMachine ss_lx_machine = { static QEMUMachine ss_lx_machine = {
.name = "LX", .name = "LX",
.desc = "Sun4m platform, SPARCstation LX", .desc = "Sun4m platform, SPARCstation LX",
.init = ss_lx_init, .init = ss_lx_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
}; };
static QEMUMachine ss4_machine = { static QEMUMachine ss4_machine = {
.name = "SS-4", .name = "SS-4",
.desc = "Sun4m platform, SPARCstation 4", .desc = "Sun4m platform, SPARCstation 4",
.init = ss4_init, .init = ss4_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
}; };
static QEMUMachine scls_machine = { static QEMUMachine scls_machine = {
.name = "SPARCClassic", .name = "SPARCClassic",
.desc = "Sun4m platform, SPARCClassic", .desc = "Sun4m platform, SPARCClassic",
.init = scls_init, .init = scls_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
}; };
static QEMUMachine sbook_machine = { static QEMUMachine sbook_machine = {
.name = "SPARCbook", .name = "SPARCbook",
.desc = "Sun4m platform, SPARCbook", .desc = "Sun4m platform, SPARCbook",
.init = sbook_init, .init = sbook_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
}; };
static const struct sun4d_hwdef sun4d_hwdefs[] = { static const struct sun4d_hwdef sun4d_hwdefs[] = {
@ -1709,7 +1709,7 @@ static QEMUMachine ss1000_machine = {
.name = "SS-1000", .name = "SS-1000",
.desc = "Sun4d platform, SPARCserver 1000", .desc = "Sun4d platform, SPARCserver 1000",
.init = ss1000_init, .init = ss1000_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
.max_cpus = 8, .max_cpus = 8,
}; };
@ -1717,7 +1717,7 @@ static QEMUMachine ss2000_machine = {
.name = "SS-2000", .name = "SS-2000",
.desc = "Sun4d platform, SPARCcenter 2000", .desc = "Sun4d platform, SPARCcenter 2000",
.init = ss2000_init, .init = ss2000_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
.max_cpus = 20, .max_cpus = 20,
}; };
@ -1896,7 +1896,7 @@ static QEMUMachine ss2_machine = {
.name = "SS-2", .name = "SS-2",
.desc = "Sun4c platform, SPARCstation 2", .desc = "Sun4c platform, SPARCstation 2",
.init = ss2_init, .init = ss2_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
}; };
static void sun4m_register_types(void) static void sun4m_register_types(void)

View File

@ -358,14 +358,14 @@ static QEMUMachine versatilepb_machine = {
.name = "versatilepb", .name = "versatilepb",
.desc = "ARM Versatile/PB (ARM926EJ-S)", .desc = "ARM Versatile/PB (ARM926EJ-S)",
.init = vpb_init, .init = vpb_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
}; };
static QEMUMachine versatileab_machine = { static QEMUMachine versatileab_machine = {
.name = "versatileab", .name = "versatileab",
.desc = "ARM Versatile/AB (ARM926EJ-S)", .desc = "ARM Versatile/AB (ARM926EJ-S)",
.init = vab_init, .init = vab_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
}; };
static void versatile_machine_init(void) static void versatile_machine_init(void)

View File

@ -477,7 +477,7 @@ static QEMUMachine vexpress_a9_machine = {
.name = "vexpress-a9", .name = "vexpress-a9",
.desc = "ARM Versatile Express for Cortex-A9", .desc = "ARM Versatile Express for Cortex-A9",
.init = vexpress_a9_init, .init = vexpress_a9_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
.max_cpus = 4, .max_cpus = 4,
}; };
@ -485,7 +485,7 @@ static QEMUMachine vexpress_a15_machine = {
.name = "vexpress-a15", .name = "vexpress-a15",
.desc = "ARM Versatile Express for Cortex-A15", .desc = "ARM Versatile Express for Cortex-A15",
.init = vexpress_a15_init, .init = vexpress_a15_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
.max_cpus = 4, .max_cpus = 4,
}; };

View File

@ -104,7 +104,6 @@ struct VirtIOBlkConf
BlockConf conf; BlockConf conf;
char *serial; char *serial;
uint32_t scsi; uint32_t scsi;
uint32_t config_wce;
}; };
#define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \ #define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \

View File

@ -895,7 +895,6 @@ static Property virtio_blk_properties[] = {
#ifdef __linux__ #ifdef __linux__
DEFINE_PROP_BIT("scsi", VirtIOPCIProxy, blk.scsi, 0, true), DEFINE_PROP_BIT("scsi", VirtIOPCIProxy, blk.scsi, 0, true),
#endif #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_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true),
DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2),
DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features), DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features),

View File

@ -66,7 +66,7 @@ int select_watchdog(const char *p)
QLIST_FOREACH(model, &watchdog_list, entry) { QLIST_FOREACH(model, &watchdog_list, entry) {
if (strcasecmp(model->wdt_name, p) == 0) { if (strcasecmp(model->wdt_name, p) == 0) {
/* add the device */ /* 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); qemu_opt_set(opts, "driver", p);
return 0; return 0;
} }

View File

@ -201,7 +201,7 @@ static QEMUMachine zynq_machine = {
.name = "xilinx-zynq-a9", .name = "xilinx-zynq-a9",
.desc = "Xilinx Zynq Platform Baseboard for Cortex-A9", .desc = "Xilinx Zynq Platform Baseboard for Cortex-A9",
.init = zynq_init, .init = zynq_init,
.use_scsi = 1, .block_default_type = IF_SCSI,
.max_cpus = 1, .max_cpus = 1,
.no_sdcard = 1 .no_sdcard = 1
}; };

View File

@ -432,11 +432,6 @@ QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque)
return aio_bh_new(qemu_aio_context, cb, 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) bool qemu_aio_wait(void)
{ {
return aio_poll(qemu_aio_context, true); return aio_poll(qemu_aio_context, true);

View File

@ -162,10 +162,6 @@ void qemu_bh_cancel(QEMUBH *bh);
*/ */
void qemu_bh_delete(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 /* Return whether there are any pending callbacks from the GSource
* attached to the AioContext. * attached to the AioContext.
* *
@ -196,7 +192,7 @@ typedef int (AioFlushHandler)(void *opaque);
/* Register a file descriptor and associated callbacks. Behaves very similarly /* Register a file descriptor and associated callbacks. Behaves very similarly
* to qemu_set_fd_handler2. Unlike qemu_set_fd_handler2, these callbacks will * 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 * Code that invokes AIO completion functions should rely on this function
* instead of qemu_set_fd_handler[2]. * 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 /* Register an event notifier and associated callbacks. Behaves very similarly
* to event_notifier_set_handler. Unlike event_notifier_set_handler, these callbacks * 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 * Code that invokes AIO completion functions should rely on this function
* instead of event_notifier_set_handler. * 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. */ /* Functions to operate on the main QEMU AioContext. */
void qemu_aio_flush(void);
bool qemu_aio_wait(void); bool qemu_aio_wait(void);
void qemu_aio_set_event_notifier(EventNotifier *notifier, void qemu_aio_set_event_notifier(EventNotifier *notifier,
EventNotifierHandler *io_read, EventNotifierHandler *io_read,

View File

@ -756,7 +756,7 @@ int qemu_global_option(const char *str)
return -1; 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, "driver", driver);
qemu_opt_set(opts, "property", property); qemu_opt_set(opts, "property", property);
qemu_opt_set(opts, "value", str+offset+1); 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); error_free(local_err);
goto out; goto out;
} }
opts = qemu_opts_create(list, NULL, 0, NULL); opts = qemu_opts_create_nofail(list);
continue; continue;
} }
if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2) { if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2) {

View File

@ -294,13 +294,14 @@ static int add_old_style_options(const char *fmt, QEMUOptionParameter *list,
static int img_create(int argc, char **argv) static int img_create(int argc, char **argv)
{ {
int c, ret = 0; int c;
uint64_t img_size = -1; uint64_t img_size = -1;
const char *fmt = "raw"; const char *fmt = "raw";
const char *base_fmt = NULL; const char *base_fmt = NULL;
const char *filename; const char *filename;
const char *base_filename = NULL; const char *base_filename = NULL;
char *options = NULL; char *options = NULL;
Error *local_err = NULL;
for(;;) { for(;;) {
c = getopt(argc, argv, "F:b:f:he6o:"); 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 " error_report("Invalid image size specified! You may use k, M, G or "
"T suffixes for "); "T suffixes for ");
error_report("kilobytes, megabytes, gigabytes and terabytes."); error_report("kilobytes, megabytes, gigabytes and terabytes.");
ret = -1; return 1;
goto out;
} }
img_size = (uint64_t)sval; img_size = (uint64_t)sval;
} }
if (options && is_help_option(options)) { if (options && is_help_option(options)) {
ret = print_block_option_help(filename, fmt); return print_block_option_help(filename, fmt);
goto out;
} }
ret = bdrv_img_create(filename, fmt, base_filename, base_fmt, bdrv_img_create(filename, fmt, base_filename, base_fmt,
options, img_size, BDRV_O_FLAGS); options, img_size, BDRV_O_FLAGS, &local_err);
out: if (error_is_set(&local_err)) {
if (ret) { error_report("%s", error_get_pretty(local_err));
error_free(local_err);
return 1; return 1;
} }
return 0; return 0;
} }
@ -1933,7 +1934,7 @@ static int img_resize(int argc, char **argv)
} }
/* Parse size */ /* 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)) { if (qemu_opt_set(param, BLOCK_OPT_SIZE, size)) {
/* Error message already printed when size parsing fails */ /* Error message already printed when size parsing fails */
ret = -1; ret = -1;

View File

@ -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) static int do_load_vmstate(char *buf, int64_t offset, int count, int *total)
{ {
*total = bdrv_load_vmstate(bs, (uint8_t *)buf, offset, count); *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" " Writes into a segment of the currently open file, using a buffer\n"
" filled with a set pattern (0xcdcdcdcd).\n" " filled with a set pattern (0xcdcdcdcd).\n"
" -b, -- write to the VM state rather than the virtual disk\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 bdrv_pwrite to write the file\n"
" -P, -- use different pattern to fill file\n" " -P, -- use different pattern to fill file\n"
" -C, -- report statistics in a machine parsable format\n" " -C, -- report statistics in a machine parsable format\n"
@ -703,7 +716,7 @@ static const cmdinfo_t write_cmd = {
.cfunc = write_f, .cfunc = write_f,
.argmin = 2, .argmin = 2,
.argmax = -1, .argmax = -1,
.args = "[-bCpqz] [-P pattern ] off len", .args = "[-bcCpqz] [-P pattern ] off len",
.oneline = "writes a number of bytes at a specified offset", .oneline = "writes a number of bytes at a specified offset",
.help = write_help, .help = write_help,
}; };
@ -712,6 +725,7 @@ static int write_f(int argc, char **argv)
{ {
struct timeval t1, t2; struct timeval t1, t2;
int Cflag = 0, pflag = 0, qflag = 0, bflag = 0, Pflag = 0, zflag = 0; int Cflag = 0, pflag = 0, qflag = 0, bflag = 0, Pflag = 0, zflag = 0;
int cflag = 0;
int c, cnt; int c, cnt;
char *buf = NULL; char *buf = NULL;
int64_t offset; int64_t offset;
@ -720,11 +734,14 @@ static int write_f(int argc, char **argv)
int total = 0; int total = 0;
int pattern = 0xcd; int pattern = 0xcd;
while ((c = getopt(argc, argv, "bCpP:qz")) != EOF) { while ((c = getopt(argc, argv, "bcCpP:qz")) != EOF) {
switch (c) { switch (c) {
case 'b': case 'b':
bflag = 1; bflag = 1;
break; break;
case 'c':
cflag = 1;
break;
case 'C': case 'C':
Cflag = 1; Cflag = 1;
break; break;
@ -801,6 +818,8 @@ static int write_f(int argc, char **argv)
cnt = do_save_vmstate(buf, offset, count, &total); cnt = do_save_vmstate(buf, offset, count, &total);
} else if (zflag) { } else if (zflag) {
cnt = do_co_write_zeroes(offset, count, &total); cnt = do_co_write_zeroes(offset, count, &total);
} else if (cflag) {
cnt = do_write_compressed(buf, offset, count, &total);
} else { } else {
cnt = do_write(buf, offset, count, &total); 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", .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) static int abort_f(int argc, char **argv)
{ {
abort(); abort();
@ -1915,6 +1995,9 @@ int main(int argc, char **argv)
add_command(&discard_cmd); add_command(&discard_cmd);
add_command(&alloc_cmd); add_command(&alloc_cmd);
add_command(&map_cmd); add_command(&map_cmd);
add_command(&break_cmd);
add_command(&resume_cmd);
add_command(&wait_break_cmd);
add_command(&abort_cmd); add_command(&abort_cmd);
add_args_command(init_args_command); add_args_command(init_args_command);

View File

@ -602,26 +602,36 @@ static void qemu_opt_del(QemuOpt *opt)
g_free(opt); g_free(opt);
} }
static void opt_set(QemuOpts *opts, const char *name, const char *value, static bool opts_accepts_any(const QemuOpts *opts)
bool prepend, Error **errp) {
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; int i;
for (i = 0; desc[i].name != NULL; i++) { for (i = 0; desc[i].name != NULL; i++) {
if (strcmp(desc[i].name, name) == 0) { if (strcmp(desc[i].name, name) == 0) {
break; return &desc[i];
} }
} }
if (desc[i].name == NULL) {
if (i == 0) { return NULL;
/* empty list -> allow any */; }
} else {
error_set(errp, QERR_INVALID_PARAMETER, name); static void opt_set(QemuOpts *opts, const char *name, const char *value,
return; 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)); opt = g_malloc0(sizeof(*opt));
@ -632,9 +642,7 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value,
} else { } else {
QTAILQ_INSERT_TAIL(&opts->head, opt, next); QTAILQ_INSERT_TAIL(&opts->head, opt, next);
} }
if (desc[i].name != NULL) { opt->desc = desc;
opt->desc = desc+i;
}
if (value) { if (value) {
opt->str = g_strdup(value); opt->str = g_strdup(value);
} }
@ -669,30 +677,43 @@ int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val)
{ {
QemuOpt *opt; QemuOpt *opt;
const QemuOptDesc *desc = opts->list->desc; 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 = 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->name = g_strdup(name);
opt->opts = opts; opt->opts = opts;
QTAILQ_INSERT_TAIL(&opts->head, opt, next);
if (desc[i].name != NULL) {
opt->desc = desc+i;
}
opt->value.boolean = !!val; 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; return 0;
} }
@ -781,6 +802,15 @@ QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
return opts; 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) void qemu_opts_reset(QemuOptsList *list)
{ {
QemuOpts *opts, *next_opts; QemuOpts *opts, *next_opts;
@ -1068,23 +1098,15 @@ void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp)
QemuOpt *opt; QemuOpt *opt;
Error *local_err = NULL; Error *local_err = NULL;
assert(opts->list->desc[0].name == NULL); assert(opts_accepts_any(opts));
QTAILQ_FOREACH(opt, &opts->head, next) { QTAILQ_FOREACH(opt, &opts->head, next) {
int i; opt->desc = find_desc_by_name(desc, opt->name);
if (!opt->desc) {
for (i = 0; desc[i].name != NULL; i++) {
if (strcmp(desc[i].name, opt->name) == 0) {
break;
}
}
if (desc[i].name == NULL) {
error_set(errp, QERR_INVALID_PARAMETER, opt->name); error_set(errp, QERR_INVALID_PARAMETER, opt->name);
return; return;
} }
opt->desc = &desc[i];
qemu_opt_parse(opt, &local_err); qemu_opt_parse(opt, &local_err);
if (error_is_set(&local_err)) { if (error_is_set(&local_err)) {
error_propagate(errp, local_err); error_propagate(errp, local_err);

View File

@ -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, void qemu_opt_set_err(QemuOpts *opts, const char *name, const char *value,
Error **errp); Error **errp);
int qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val); 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); 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 qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque,
int abort_on_failure); 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_find(QemuOptsList *list, const char *id);
QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id,
int fail_if_exists, Error **errp); int fail_if_exists, Error **errp);
QemuOpts *qemu_opts_create_nofail(QemuOptsList *list);
void qemu_opts_reset(QemuOptsList *list); void qemu_opts_reset(QemuOptsList *list);
void qemu_opts_loc_restore(QemuOpts *opts); void qemu_opts_loc_restore(QemuOpts *opts);
int qemu_opts_set(QemuOptsList *list, const char *id, int qemu_opts_set(QemuOptsList *list, const char *id,

View File

@ -579,7 +579,7 @@ int inet_listen(const char *str, char *ostr, int olen,
addr = inet_parse(str, errp); addr = inet_parse(str, errp);
if (addr != NULL) { 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); inet_addr_to_opts(opts, addr);
qapi_free_InetSocketAddress(addr); qapi_free_InetSocketAddress(addr);
sock = inet_listen_opts(opts, port_offset, errp); 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); addr = inet_parse(str, errp);
if (addr != NULL) { 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); inet_addr_to_opts(opts, addr);
qapi_free_InetSocketAddress(addr); qapi_free_InetSocketAddress(addr);
sock = inet_connect_opts(opts, errp, NULL, NULL); sock = inet_connect_opts(opts, errp, NULL, NULL);
@ -652,7 +652,7 @@ int inet_nonblocking_connect(const char *str,
addr = inet_parse(str, errp); addr = inet_parse(str, errp);
if (addr != NULL) { 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); inet_addr_to_opts(opts, addr);
qapi_free_InetSocketAddress(addr); qapi_free_InetSocketAddress(addr);
sock = inet_connect_opts(opts, errp, callback, opaque); 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; char *path, *optstr;
int sock, len; int sock, len;
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); opts = qemu_opts_create_nofail(&dummy_opts);
optstr = strchr(str, ','); optstr = strchr(str, ',');
if (optstr) { if (optstr) {
@ -823,7 +823,7 @@ int unix_connect(const char *path, Error **errp)
QemuOpts *opts; QemuOpts *opts;
int sock; int sock;
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); opts = qemu_opts_create_nofail(&dummy_opts);
qemu_opt_set(opts, "path", path); qemu_opt_set(opts, "path", path);
sock = unix_connect_opts(opts, errp, NULL, NULL); sock = unix_connect_opts(opts, errp, NULL, NULL);
qemu_opts_del(opts); qemu_opts_del(opts);
@ -840,7 +840,7 @@ int unix_nonblocking_connect(const char *path,
g_assert(callback != NULL); 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); qemu_opt_set(opts, "path", path);
sock = unix_connect_opts(opts, errp, callback, opaque); sock = unix_connect_opts(opts, errp, callback, opaque);
qemu_opts_del(opts); qemu_opts_del(opts);
@ -891,7 +891,7 @@ int socket_connect(SocketAddress *addr, Error **errp,
QemuOpts *opts; QemuOpts *opts;
int fd; int fd;
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); opts = qemu_opts_create_nofail(&dummy_opts);
switch (addr->kind) { switch (addr->kind) {
case SOCKET_ADDRESS_KIND_INET: case SOCKET_ADDRESS_KIND_INET:
inet_addr_to_opts(opts, addr->inet); inet_addr_to_opts(opts, addr->inet);
@ -922,7 +922,7 @@ int socket_listen(SocketAddress *addr, Error **errp)
QemuOpts *opts; QemuOpts *opts;
int fd; int fd;
opts = qemu_opts_create(&dummy_opts, NULL, 0, NULL); opts = qemu_opts_create_nofail(&dummy_opts);
switch (addr->kind) { switch (addr->kind) {
case SOCKET_ADDRESS_KIND_INET: case SOCKET_ADDRESS_KIND_INET:
inet_addr_to_opts(opts, addr->inet); inet_addr_to_opts(opts, addr->inet);

129
tests/qemu-iotests/045 Executable file
View 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'])

View File

@ -0,0 +1,5 @@
......
----------------------------------------------------------------------
Ran 6 tests
OK

215
tests/qemu-iotests/046 Executable file
View 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
View 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

View File

@ -51,3 +51,5 @@
042 rw auto quick 042 rw auto quick
043 rw auto backing 043 rw auto backing
044 rw auto 044 rw auto
045 rw auto
046 rw auto aio

View File

@ -79,6 +79,18 @@ class VM(object):
self._num_drives += 1 self._num_drives += 1
return self 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): def launch(self):
'''Launch the VM and establish a QMP connection''' '''Launch the VM and establish a QMP connection'''
devnull = open('/dev/null', 'rb') devnull = open('/dev/null', 'rb')

View File

@ -15,6 +15,14 @@
AioContext *ctx; 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. */ /* Simple callbacks for testing. */
typedef struct { typedef struct {
@ -78,14 +86,6 @@ static void test_notify(void)
g_assert(!aio_poll(ctx, false)); 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) static void test_bh_schedule(void)
{ {
BHTestData data = { .n = 0 }; BHTestData data = { .n = 0 };
@ -116,7 +116,7 @@ static void test_bh_schedule10(void)
g_assert(aio_poll(ctx, true)); g_assert(aio_poll(ctx, true));
g_assert_cmpint(data.n, ==, 2); g_assert_cmpint(data.n, ==, 2);
aio_flush(ctx); wait_for_aio();
g_assert_cmpint(data.n, ==, 10); g_assert_cmpint(data.n, ==, 10);
g_assert(!aio_poll(ctx, false)); g_assert(!aio_poll(ctx, false));
@ -164,7 +164,7 @@ static void test_bh_delete_from_cb(void)
qemu_bh_schedule(data1.bh); qemu_bh_schedule(data1.bh);
g_assert_cmpint(data1.n, ==, 0); g_assert_cmpint(data1.n, ==, 0);
aio_flush(ctx); wait_for_aio();
g_assert_cmpint(data1.n, ==, data1.max); g_assert_cmpint(data1.n, ==, data1.max);
g_assert(data1.bh == NULL); 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_cmpint(data4.n, ==, 1);
g_assert(data1.bh == NULL); g_assert(data1.bh == NULL);
aio_flush(ctx); wait_for_aio();
g_assert_cmpint(data1.n, ==, data1.max); g_assert_cmpint(data1.n, ==, data1.max);
g_assert_cmpint(data2.n, ==, data2.max); g_assert_cmpint(data2.n, ==, data2.max);
g_assert_cmpint(data3.n, ==, data3.max); g_assert_cmpint(data3.n, ==, data3.max);
@ -219,7 +219,7 @@ static void test_bh_flush(void)
qemu_bh_schedule(data.bh); qemu_bh_schedule(data.bh);
g_assert_cmpint(data.n, ==, 0); g_assert_cmpint(data.n, ==, 0);
aio_flush(ctx); wait_for_aio();
g_assert_cmpint(data.n, ==, 1); g_assert_cmpint(data.n, ==, 1);
g_assert(!aio_poll(ctx, false)); 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_cmpint(data.active, ==, 9);
g_assert(aio_poll(ctx, false)); g_assert(aio_poll(ctx, false));
aio_flush(ctx); wait_for_aio();
g_assert_cmpint(data.n, ==, 10); g_assert_cmpint(data.n, ==, 10);
g_assert_cmpint(data.active, ==, 0); g_assert_cmpint(data.active, ==, 0);
g_assert(!aio_poll(ctx, false)); g_assert(!aio_poll(ctx, false));
@ -325,7 +325,7 @@ static void test_wait_event_notifier_noflush(void)
g_assert_cmpint(data.n, ==, 2); g_assert_cmpint(data.n, ==, 2);
event_notifier_set(&dummy.e); event_notifier_set(&dummy.e);
aio_flush(ctx); wait_for_aio();
g_assert_cmpint(data.n, ==, 2); g_assert_cmpint(data.n, ==, 2);
g_assert_cmpint(dummy.n, ==, 1); g_assert_cmpint(dummy.n, ==, 1);
g_assert_cmpint(dummy.active, ==, 0); 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 * - sometimes both the AioContext and the glib main loop wake
* themselves up. Hence, some "g_assert(!aio_poll(ctx, false));" * themselves up. Hence, some "g_assert(!aio_poll(ctx, false));"
* are replaced by "while (g_main_context_iteration(NULL, 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, * "while (g_main_context_iteration(NULL, true)" seems to work,
* but it is not documented _why_ it works. For these tests a * but it is not documented _why_ it works. For these tests a
* non-blocking loop like "while (g_main_context_iteration(NULL, false)" * 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_init(&argc, &argv, NULL);
g_test_add_func("/aio/notify", test_notify); 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/schedule", test_bh_schedule);
g_test_add_func("/aio/bh/schedule10", test_bh_schedule10); g_test_add_func("/aio/bh/schedule10", test_bh_schedule10);
g_test_add_func("/aio/bh/cancel", test_bh_cancel); g_test_add_func("/aio/bh/cancel", test_bh_cancel);

View File

@ -47,11 +47,19 @@ static void qemu_aio_wait_nonblocking(void)
qemu_aio_wait(); 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) static void test_submit(void)
{ {
WorkerTestData data = { .n = 0 }; WorkerTestData data = { .n = 0 };
thread_pool_submit(worker_cb, &data); thread_pool_submit(worker_cb, &data);
qemu_aio_flush(); qemu_aio_wait_all();
g_assert_cmpint(data.n, ==, 1); 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. */ /* The callbacks are not called until after the first wait. */
active = 1; active = 1;
g_assert_cmpint(data.ret, ==, -EINPROGRESS); g_assert_cmpint(data.ret, ==, -EINPROGRESS);
qemu_aio_flush(); qemu_aio_wait_all();
g_assert_cmpint(active, ==, 0); g_assert_cmpint(active, ==, 0);
g_assert_cmpint(data.n, ==, 1); g_assert_cmpint(data.n, ==, 1);
g_assert_cmpint(data.ret, ==, 0); g_assert_cmpint(data.ret, ==, 0);
@ -84,7 +92,7 @@ static void co_test_cb(void *opaque)
data->ret = 0; data->ret = 0;
active--; 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) static void test_submit_co(void)
@ -99,9 +107,9 @@ static void test_submit_co(void)
g_assert_cmpint(active, ==, 1); g_assert_cmpint(active, ==, 1);
g_assert_cmpint(data.ret, ==, -EINPROGRESS); 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. */ /* Back here after the coroutine has finished. */
@ -184,7 +192,7 @@ static void test_cancel(void)
} }
/* Finish execution and execute any remaining callbacks. */ /* Finish execution and execute any remaining callbacks. */
qemu_aio_flush(); qemu_aio_wait_all();
g_assert_cmpint(active, ==, 0); g_assert_cmpint(active, ==, 0);
for (i = 0; i < 100; i++) { for (i = 0; i < 100; i++) {
if (data[i].n == 3) { if (data[i].n == 3) {

41
vl.c
View File

@ -886,9 +886,9 @@ static int cleanup_add_fd(QemuOpts *opts, void *opaque)
static int drive_init_func(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) static int drive_enable_snapshot(QemuOpts *opts, void *opaque)
@ -899,16 +899,11 @@ static int drive_enable_snapshot(QemuOpts *opts, void *opaque)
return 0; return 0;
} }
static void default_drive(int enable, int snapshot, int use_scsi, static void default_drive(int enable, int snapshot, BlockInterfaceType type,
BlockInterfaceType type, int index, int index, const char *optstr)
const char *optstr)
{ {
QemuOpts *opts; QemuOpts *opts;
if (type == IF_DEFAULT) {
type = use_scsi ? IF_SCSI : IF_IDE;
}
if (!enable || drive_get_by_index(type, index)) { if (!enable || drive_get_by_index(type, index)) {
return; return;
} }
@ -917,7 +912,7 @@ static void default_drive(int enable, int snapshot, int use_scsi,
if (snapshot) { if (snapshot) {
drive_enable_snapshot(opts, NULL); drive_enable_snapshot(opts, NULL);
} }
if (!drive_init(opts, use_scsi)) { if (!drive_init(opts, type)) {
exit(1); exit(1);
} }
} }
@ -2001,7 +1996,7 @@ static int balloon_parse(const char *arg)
return -1; return -1;
} else { } else {
/* create empty opts */ /* 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"); qemu_opt_set(opts, "driver", "virtio-balloon");
return 0; return 0;
@ -2251,14 +2246,14 @@ static int virtcon_parse(const char *devname)
exit(1); exit(1);
} }
bus_opts = qemu_opts_create(device, NULL, 0, NULL); bus_opts = qemu_opts_create_nofail(device);
if (arch_type == QEMU_ARCH_S390X) { if (arch_type == QEMU_ARCH_S390X) {
qemu_opt_set(bus_opts, "driver", "virtio-serial-s390"); qemu_opt_set(bus_opts, "driver", "virtio-serial-s390");
} else { } else {
qemu_opt_set(bus_opts, "driver", "virtio-serial-pci"); 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"); qemu_opt_set(dev_opts, "driver", "virtconsole");
snprintf(label, sizeof(label), "virtcon%d", index); 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_set_bool(fsdev, "readonly",
qemu_opt_get_bool(opts, "readonly", 0)); qemu_opt_get_bool(opts, "readonly", 0));
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0, device = qemu_opts_create_nofail(qemu_find_opts("device"));
NULL);
qemu_opt_set(device, "driver", "virtio-9p-pci"); qemu_opt_set(device, "driver", "virtio-9p-pci");
qemu_opt_set(device, "fsdev", qemu_opt_set(device, "fsdev",
qemu_opt_get(opts, "mount_tag")); qemu_opt_get(opts, "mount_tag"));
@ -3131,8 +3125,7 @@ int main(int argc, char **argv, char **envp)
} }
qemu_opt_set(fsdev, "fsdriver", "synth"); qemu_opt_set(fsdev, "fsdriver", "synth");
device = qemu_opts_create(qemu_find_opts("device"), NULL, 0, device = qemu_opts_create_nofail(qemu_find_opts("device"));
NULL);
qemu_opt_set(device, "driver", "virtio-9p-pci"); qemu_opt_set(device, "driver", "virtio-9p-pci");
qemu_opt_set(device, "fsdev", "v_synth"); qemu_opt_set(device, "fsdev", "v_synth");
qemu_opt_set(device, "mount_tag", "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 */ /* open the virtual block devices */
if (snapshot) if (snapshot)
qemu_opts_foreach(qemu_find_opts("drive"), drive_enable_snapshot, NULL, 0); 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); exit(1);
}
default_drive(default_cdrom, snapshot, machine->use_scsi, default_drive(default_cdrom, snapshot, machine->block_default_type, 2,
IF_DEFAULT, 2, CDROM_OPTS); CDROM_OPTS);
default_drive(default_floppy, snapshot, machine->use_scsi, default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS);
IF_FLOPPY, 0, FD_OPTS); default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS);
default_drive(default_sdcard, snapshot, machine->use_scsi,
IF_SD, 0, SD_OPTS);
register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL); register_savevm_live(NULL, "ram", 0, 4, &savevm_ram_handlers, NULL);