mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -215,8 +215,3 @@ void aio_context_unref(AioContext *ctx)
|
||||
{
|
||||
g_source_unref(&ctx->source);
|
||||
}
|
||||
|
||||
void aio_flush(AioContext *ctx)
|
||||
{
|
||||
while (aio_poll(ctx, true));
|
||||
}
|
||||
|
||||
@@ -518,22 +518,16 @@ BlockDriver *bdrv_find_protocol(const char *filename)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int find_image_format(const char *filename, BlockDriver **pdrv)
|
||||
static int find_image_format(BlockDriverState *bs, const char *filename,
|
||||
BlockDriver **pdrv)
|
||||
{
|
||||
int ret, score, score_max;
|
||||
int score, score_max;
|
||||
BlockDriver *drv1, *drv;
|
||||
uint8_t buf[2048];
|
||||
BlockDriverState *bs;
|
||||
|
||||
ret = bdrv_file_open(&bs, filename, 0);
|
||||
if (ret < 0) {
|
||||
*pdrv = NULL;
|
||||
return ret;
|
||||
}
|
||||
int ret = 0;
|
||||
|
||||
/* Return the raw BlockDriver * to scsi-generic devices or empty drives */
|
||||
if (bs->sg || !bdrv_is_inserted(bs)) {
|
||||
bdrv_delete(bs);
|
||||
drv = bdrv_find_format("raw");
|
||||
if (!drv) {
|
||||
ret = -ENOENT;
|
||||
@@ -543,7 +537,6 @@ static int find_image_format(const char *filename, BlockDriver **pdrv)
|
||||
}
|
||||
|
||||
ret = bdrv_pread(bs, 0, buf, sizeof(buf));
|
||||
bdrv_delete(bs);
|
||||
if (ret < 0) {
|
||||
*pdrv = NULL;
|
||||
return ret;
|
||||
@@ -634,10 +627,31 @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
|
||||
bs->copy_on_read--;
|
||||
}
|
||||
|
||||
static int bdrv_open_flags(BlockDriverState *bs, int flags)
|
||||
{
|
||||
int open_flags = flags | BDRV_O_CACHE_WB;
|
||||
|
||||
/*
|
||||
* Clear flags that are internal to the block layer before opening the
|
||||
* image.
|
||||
*/
|
||||
open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
||||
|
||||
/*
|
||||
* Snapshots should be writable.
|
||||
*/
|
||||
if (bs->is_temporary) {
|
||||
open_flags |= BDRV_O_RDWR;
|
||||
}
|
||||
|
||||
return open_flags;
|
||||
}
|
||||
|
||||
/*
|
||||
* Common part for opening disk images and files
|
||||
*/
|
||||
static int bdrv_open_common(BlockDriverState *bs, const char *filename,
|
||||
static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file,
|
||||
const char *filename,
|
||||
int flags, BlockDriver *drv)
|
||||
{
|
||||
int ret, open_flags;
|
||||
@@ -665,31 +679,22 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
|
||||
bs->opaque = g_malloc0(drv->instance_size);
|
||||
|
||||
bs->enable_write_cache = !!(flags & BDRV_O_CACHE_WB);
|
||||
open_flags = flags | BDRV_O_CACHE_WB;
|
||||
|
||||
/*
|
||||
* Clear flags that are internal to the block layer before opening the
|
||||
* image.
|
||||
*/
|
||||
open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
|
||||
|
||||
/*
|
||||
* Snapshots should be writable.
|
||||
*/
|
||||
if (bs->is_temporary) {
|
||||
open_flags |= BDRV_O_RDWR;
|
||||
}
|
||||
open_flags = bdrv_open_flags(bs, flags);
|
||||
|
||||
bs->read_only = !(open_flags & BDRV_O_RDWR);
|
||||
|
||||
/* Open the image, either directly or using a protocol */
|
||||
if (drv->bdrv_file_open) {
|
||||
ret = drv->bdrv_file_open(bs, filename, open_flags);
|
||||
} else {
|
||||
ret = bdrv_file_open(&bs->file, filename, open_flags);
|
||||
if (ret >= 0) {
|
||||
ret = drv->bdrv_open(bs, open_flags);
|
||||
if (file != NULL) {
|
||||
bdrv_swap(file, bs);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = drv->bdrv_file_open(bs, filename, open_flags);
|
||||
}
|
||||
} else {
|
||||
assert(file != NULL);
|
||||
bs->file = file;
|
||||
ret = drv->bdrv_open(bs, open_flags);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
@@ -709,10 +714,7 @@ static int bdrv_open_common(BlockDriverState *bs, const char *filename,
|
||||
return 0;
|
||||
|
||||
free_and_fail:
|
||||
if (bs->file) {
|
||||
bdrv_delete(bs->file);
|
||||
bs->file = NULL;
|
||||
}
|
||||
bs->file = NULL;
|
||||
g_free(bs->opaque);
|
||||
bs->opaque = NULL;
|
||||
bs->drv = NULL;
|
||||
@@ -734,7 +736,7 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, int flags)
|
||||
}
|
||||
|
||||
bs = bdrv_new("");
|
||||
ret = bdrv_open_common(bs, filename, flags, drv);
|
||||
ret = bdrv_open_common(bs, NULL, filename, flags, drv);
|
||||
if (ret < 0) {
|
||||
bdrv_delete(bs);
|
||||
return ret;
|
||||
@@ -789,6 +791,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
||||
int ret;
|
||||
/* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */
|
||||
char tmp_filename[PATH_MAX + 1];
|
||||
BlockDriverState *file = NULL;
|
||||
|
||||
if (flags & BDRV_O_SNAPSHOT) {
|
||||
BlockDriverState *bs1;
|
||||
@@ -848,25 +851,36 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
||||
bs->is_temporary = 1;
|
||||
}
|
||||
|
||||
/* Find the right image format driver */
|
||||
if (!drv) {
|
||||
ret = find_image_format(filename, &drv);
|
||||
}
|
||||
|
||||
if (!drv) {
|
||||
goto unlink_and_fail;
|
||||
}
|
||||
|
||||
/* Open image file without format layer */
|
||||
if (flags & BDRV_O_RDWR) {
|
||||
flags |= BDRV_O_ALLOW_RDWR;
|
||||
}
|
||||
|
||||
ret = bdrv_file_open(&file, filename, bdrv_open_flags(bs, flags));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Find the right image format driver */
|
||||
if (!drv) {
|
||||
ret = find_image_format(file, filename, &drv);
|
||||
}
|
||||
|
||||
if (!drv) {
|
||||
goto unlink_and_fail;
|
||||
}
|
||||
|
||||
/* Open the image */
|
||||
ret = bdrv_open_common(bs, filename, flags, drv);
|
||||
ret = bdrv_open_common(bs, file, filename, flags, drv);
|
||||
if (ret < 0) {
|
||||
goto unlink_and_fail;
|
||||
}
|
||||
|
||||
if (bs->file != file) {
|
||||
bdrv_delete(file);
|
||||
file = NULL;
|
||||
}
|
||||
|
||||
/* If there is a backing file, use it */
|
||||
if ((flags & BDRV_O_NO_BACKING) == 0) {
|
||||
ret = bdrv_open_backing_file(bs);
|
||||
@@ -888,6 +902,9 @@ int bdrv_open(BlockDriverState *bs, const char *filename, int flags,
|
||||
return 0;
|
||||
|
||||
unlink_and_fail:
|
||||
if (file != NULL) {
|
||||
bdrv_delete(file);
|
||||
}
|
||||
if (bs->is_temporary) {
|
||||
unlink(filename);
|
||||
}
|
||||
@@ -3028,7 +3045,46 @@ void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
|
||||
}
|
||||
|
||||
drv->bdrv_debug_event(bs, event);
|
||||
}
|
||||
|
||||
int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
const char *tag)
|
||||
{
|
||||
while (bs && bs->drv && !bs->drv->bdrv_debug_breakpoint) {
|
||||
bs = bs->file;
|
||||
}
|
||||
|
||||
if (bs && bs->drv && bs->drv->bdrv_debug_breakpoint) {
|
||||
return bs->drv->bdrv_debug_breakpoint(bs, event, tag);
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int bdrv_debug_resume(BlockDriverState *bs, const char *tag)
|
||||
{
|
||||
while (bs && bs->drv && !bs->drv->bdrv_debug_resume) {
|
||||
bs = bs->file;
|
||||
}
|
||||
|
||||
if (bs && bs->drv && bs->drv->bdrv_debug_resume) {
|
||||
return bs->drv->bdrv_debug_resume(bs, tag);
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag)
|
||||
{
|
||||
while (bs && bs->drv && !bs->drv->bdrv_debug_is_suspended) {
|
||||
bs = bs->file;
|
||||
}
|
||||
|
||||
if (bs && bs->drv && bs->drv->bdrv_debug_is_suspended) {
|
||||
return bs->drv->bdrv_debug_is_suspended(bs, tag);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**************************************************************/
|
||||
@@ -3778,12 +3834,20 @@ typedef struct BlockDriverAIOCBCoroutine {
|
||||
BlockDriverAIOCB common;
|
||||
BlockRequest req;
|
||||
bool is_write;
|
||||
bool *done;
|
||||
QEMUBH* bh;
|
||||
} BlockDriverAIOCBCoroutine;
|
||||
|
||||
static void bdrv_aio_co_cancel_em(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
qemu_aio_flush();
|
||||
BlockDriverAIOCBCoroutine *acb =
|
||||
container_of(blockacb, BlockDriverAIOCBCoroutine, common);
|
||||
bool done = false;
|
||||
|
||||
acb->done = &done;
|
||||
while (!done) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
}
|
||||
|
||||
static const AIOCBInfo bdrv_em_co_aiocb_info = {
|
||||
@@ -3796,6 +3860,11 @@ static void bdrv_co_em_bh(void *opaque)
|
||||
BlockDriverAIOCBCoroutine *acb = opaque;
|
||||
|
||||
acb->common.cb(acb->common.opaque, acb->req.error);
|
||||
|
||||
if (acb->done) {
|
||||
*acb->done = true;
|
||||
}
|
||||
|
||||
qemu_bh_delete(acb->bh);
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
@@ -3834,6 +3903,7 @@ static BlockDriverAIOCB *bdrv_co_aio_rw_vector(BlockDriverState *bs,
|
||||
acb->req.nb_sectors = nb_sectors;
|
||||
acb->req.qiov = qiov;
|
||||
acb->is_write = is_write;
|
||||
acb->done = NULL;
|
||||
|
||||
co = qemu_coroutine_create(bdrv_co_do_rw);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
@@ -3860,6 +3930,8 @@ BlockDriverAIOCB *bdrv_aio_flush(BlockDriverState *bs,
|
||||
BlockDriverAIOCBCoroutine *acb;
|
||||
|
||||
acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
|
||||
acb->done = NULL;
|
||||
|
||||
co = qemu_coroutine_create(bdrv_aio_flush_co_entry);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
|
||||
@@ -3888,6 +3960,7 @@ BlockDriverAIOCB *bdrv_aio_discard(BlockDriverState *bs,
|
||||
acb = qemu_aio_get(&bdrv_em_co_aiocb_info, bs, cb, opaque);
|
||||
acb->req.sector = sector_num;
|
||||
acb->req.nb_sectors = nb_sectors;
|
||||
acb->done = NULL;
|
||||
co = qemu_coroutine_create(bdrv_aio_discard_co_entry);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
|
||||
@@ -4408,9 +4481,9 @@ bdrv_acct_done(BlockDriverState *bs, BlockAcctCookie *cookie)
|
||||
bs->total_time_ns[cookie->type] += get_clock() - cookie->start_time_ns;
|
||||
}
|
||||
|
||||
int bdrv_img_create(const char *filename, const char *fmt,
|
||||
const char *base_filename, const char *base_fmt,
|
||||
char *options, uint64_t img_size, int flags)
|
||||
void bdrv_img_create(const char *filename, const char *fmt,
|
||||
const char *base_filename, const char *base_fmt,
|
||||
char *options, uint64_t img_size, int flags, Error **errp)
|
||||
{
|
||||
QEMUOptionParameter *param = NULL, *create_options = NULL;
|
||||
QEMUOptionParameter *backing_fmt, *backing_file, *size;
|
||||
@@ -4422,16 +4495,14 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
/* Find driver and parse its options */
|
||||
drv = bdrv_find_format(fmt);
|
||||
if (!drv) {
|
||||
error_report("Unknown file format '%s'", fmt);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
error_setg(errp, "Unknown file format '%s'", fmt);
|
||||
return;
|
||||
}
|
||||
|
||||
proto_drv = bdrv_find_protocol(filename);
|
||||
if (!proto_drv) {
|
||||
error_report("Unknown protocol '%s'", filename);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
error_setg(errp, "Unknown protocol '%s'", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
create_options = append_option_parameters(create_options,
|
||||
@@ -4448,8 +4519,7 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
if (options) {
|
||||
param = parse_option_parameters(options, create_options, param);
|
||||
if (param == NULL) {
|
||||
error_report("Invalid options for file format '%s'.", fmt);
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Invalid options for file format '%s'.", fmt);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -4457,18 +4527,16 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
if (base_filename) {
|
||||
if (set_option_parameter(param, BLOCK_OPT_BACKING_FILE,
|
||||
base_filename)) {
|
||||
error_report("Backing file not supported for file format '%s'",
|
||||
fmt);
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Backing file not supported for file format '%s'",
|
||||
fmt);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (base_fmt) {
|
||||
if (set_option_parameter(param, BLOCK_OPT_BACKING_FMT, base_fmt)) {
|
||||
error_report("Backing file format not supported for file "
|
||||
"format '%s'", fmt);
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Backing file format not supported for file "
|
||||
"format '%s'", fmt);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -4476,9 +4544,8 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
backing_file = get_option_parameter(param, BLOCK_OPT_BACKING_FILE);
|
||||
if (backing_file && backing_file->value.s) {
|
||||
if (!strcmp(filename, backing_file->value.s)) {
|
||||
error_report("Error: Trying to create an image with the "
|
||||
"same filename as the backing file");
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Error: Trying to create an image with the "
|
||||
"same filename as the backing file");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -4487,9 +4554,8 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
if (backing_fmt && backing_fmt->value.s) {
|
||||
backing_drv = bdrv_find_format(backing_fmt->value.s);
|
||||
if (!backing_drv) {
|
||||
error_report("Unknown backing file format '%s'",
|
||||
backing_fmt->value.s);
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Unknown backing file format '%s'",
|
||||
backing_fmt->value.s);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -4511,7 +4577,8 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
|
||||
ret = bdrv_open(bs, backing_file->value.s, back_flags, backing_drv);
|
||||
if (ret < 0) {
|
||||
error_report("Could not open '%s'", backing_file->value.s);
|
||||
error_setg_errno(errp, -ret, "Could not open '%s'",
|
||||
backing_file->value.s);
|
||||
goto out;
|
||||
}
|
||||
bdrv_get_geometry(bs, &size);
|
||||
@@ -4520,8 +4587,7 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
snprintf(buf, sizeof(buf), "%" PRId64, size);
|
||||
set_option_parameter(param, BLOCK_OPT_SIZE, buf);
|
||||
} else {
|
||||
error_report("Image creation needs a size parameter");
|
||||
ret = -EINVAL;
|
||||
error_setg(errp, "Image creation needs a size parameter");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -4531,17 +4597,16 @@ int bdrv_img_create(const char *filename, const char *fmt,
|
||||
puts("");
|
||||
|
||||
ret = bdrv_create(drv, filename, param);
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOTSUP) {
|
||||
error_report("Formatting or formatting option not supported for "
|
||||
"file format '%s'", fmt);
|
||||
error_setg(errp,"Formatting or formatting option not supported for "
|
||||
"file format '%s'", fmt);
|
||||
} else if (ret == -EFBIG) {
|
||||
error_report("The image size is too large for file format '%s'",
|
||||
fmt);
|
||||
error_setg(errp, "The image size is too large for file format '%s'",
|
||||
fmt);
|
||||
} else {
|
||||
error_report("%s: error while creating %s: %s", filename, fmt,
|
||||
strerror(-ret));
|
||||
error_setg(errp, "%s: error while creating %s: %s", filename, fmt,
|
||||
strerror(-ret));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4552,6 +4617,4 @@ out:
|
||||
if (bs) {
|
||||
bdrv_delete(bs);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -343,9 +343,9 @@ int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
|
||||
int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
|
||||
int64_t pos, int size);
|
||||
|
||||
int bdrv_img_create(const char *filename, const char *fmt,
|
||||
const char *base_filename, const char *base_fmt,
|
||||
char *options, uint64_t img_size, int flags);
|
||||
void bdrv_img_create(const char *filename, const char *fmt,
|
||||
const char *base_filename, const char *base_fmt,
|
||||
char *options, uint64_t img_size, int flags, Error **errp);
|
||||
|
||||
void bdrv_set_buffer_alignment(BlockDriverState *bs, int align);
|
||||
void *qemu_blockalign(BlockDriverState *bs, size_t size);
|
||||
@@ -431,4 +431,9 @@ typedef enum {
|
||||
#define BLKDBG_EVENT(bs, evt) bdrv_debug_event(bs, evt)
|
||||
void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event);
|
||||
|
||||
int bdrv_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
const char *tag);
|
||||
int bdrv_debug_resume(BlockDriverState *bs, const char *tag);
|
||||
bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag);
|
||||
|
||||
#endif
|
||||
|
||||
+123
-5
@@ -29,8 +29,10 @@
|
||||
typedef struct BDRVBlkdebugState {
|
||||
int state;
|
||||
int new_state;
|
||||
|
||||
QLIST_HEAD(, BlkdebugRule) rules[BLKDBG_EVENT_MAX];
|
||||
QSIMPLEQ_HEAD(, BlkdebugRule) active_rules;
|
||||
QLIST_HEAD(, BlkdebugSuspendedReq) suspended_reqs;
|
||||
} BDRVBlkdebugState;
|
||||
|
||||
typedef struct BlkdebugAIOCB {
|
||||
@@ -39,6 +41,12 @@ typedef struct BlkdebugAIOCB {
|
||||
int ret;
|
||||
} BlkdebugAIOCB;
|
||||
|
||||
typedef struct BlkdebugSuspendedReq {
|
||||
Coroutine *co;
|
||||
char *tag;
|
||||
QLIST_ENTRY(BlkdebugSuspendedReq) next;
|
||||
} BlkdebugSuspendedReq;
|
||||
|
||||
static void blkdebug_aio_cancel(BlockDriverAIOCB *blockacb);
|
||||
|
||||
static const AIOCBInfo blkdebug_aiocb_info = {
|
||||
@@ -49,6 +57,7 @@ static const AIOCBInfo blkdebug_aiocb_info = {
|
||||
enum {
|
||||
ACTION_INJECT_ERROR,
|
||||
ACTION_SET_STATE,
|
||||
ACTION_SUSPEND,
|
||||
};
|
||||
|
||||
typedef struct BlkdebugRule {
|
||||
@@ -65,6 +74,9 @@ typedef struct BlkdebugRule {
|
||||
struct {
|
||||
int new_state;
|
||||
} set_state;
|
||||
struct {
|
||||
char *tag;
|
||||
} suspend;
|
||||
} options;
|
||||
QLIST_ENTRY(BlkdebugRule) next;
|
||||
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
|
||||
@@ -226,6 +238,11 @@ static int add_rule(QemuOpts *opts, void *opaque)
|
||||
rule->options.set_state.new_state =
|
||||
qemu_opt_get_number(opts, "new_state", 0);
|
||||
break;
|
||||
|
||||
case ACTION_SUSPEND:
|
||||
rule->options.suspend.tag =
|
||||
g_strdup(qemu_opt_get(opts, "tag"));
|
||||
break;
|
||||
};
|
||||
|
||||
/* Add the rule */
|
||||
@@ -234,12 +251,32 @@ static int add_rule(QemuOpts *opts, void *opaque)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void remove_rule(BlkdebugRule *rule)
|
||||
{
|
||||
switch (rule->action) {
|
||||
case ACTION_INJECT_ERROR:
|
||||
case ACTION_SET_STATE:
|
||||
break;
|
||||
case ACTION_SUSPEND:
|
||||
g_free(rule->options.suspend.tag);
|
||||
break;
|
||||
}
|
||||
|
||||
QLIST_REMOVE(rule, next);
|
||||
g_free(rule);
|
||||
}
|
||||
|
||||
static int read_config(BDRVBlkdebugState *s, const char *filename)
|
||||
{
|
||||
FILE *f;
|
||||
int ret;
|
||||
struct add_rule_data d;
|
||||
|
||||
/* Allow usage without config file */
|
||||
if (!*filename) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
f = fopen(filename, "r");
|
||||
if (f == NULL) {
|
||||
return -errno;
|
||||
@@ -389,6 +426,7 @@ static BlockDriverAIOCB *blkdebug_aio_writev(BlockDriverState *bs,
|
||||
return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, cb, opaque);
|
||||
}
|
||||
|
||||
|
||||
static void blkdebug_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
@@ -397,12 +435,32 @@ static void blkdebug_close(BlockDriverState *bs)
|
||||
|
||||
for (i = 0; i < BLKDBG_EVENT_MAX; i++) {
|
||||
QLIST_FOREACH_SAFE(rule, &s->rules[i], next, next) {
|
||||
QLIST_REMOVE(rule, next);
|
||||
g_free(rule);
|
||||
remove_rule(rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void suspend_request(BlockDriverState *bs, BlkdebugRule *rule)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugSuspendedReq r;
|
||||
|
||||
r = (BlkdebugSuspendedReq) {
|
||||
.co = qemu_coroutine_self(),
|
||||
.tag = g_strdup(rule->options.suspend.tag),
|
||||
};
|
||||
|
||||
remove_rule(rule);
|
||||
QLIST_INSERT_HEAD(&s->suspended_reqs, &r, next);
|
||||
|
||||
printf("blkdebug: Suspended request '%s'\n", r.tag);
|
||||
qemu_coroutine_yield();
|
||||
printf("blkdebug: Resuming request '%s'\n", r.tag);
|
||||
|
||||
QLIST_REMOVE(&r, next);
|
||||
g_free(r.tag);
|
||||
}
|
||||
|
||||
static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
bool injected)
|
||||
{
|
||||
@@ -426,6 +484,10 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
case ACTION_SET_STATE:
|
||||
s->new_state = rule->options.set_state.new_state;
|
||||
break;
|
||||
|
||||
case ACTION_SUSPEND:
|
||||
suspend_request(bs, rule);
|
||||
break;
|
||||
}
|
||||
return injected;
|
||||
}
|
||||
@@ -433,19 +495,72 @@ static bool process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
|
||||
static void blkdebug_debug_event(BlockDriverState *bs, BlkDebugEvent event)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
struct BlkdebugRule *rule;
|
||||
struct BlkdebugRule *rule, *next;
|
||||
bool injected;
|
||||
|
||||
assert((int)event >= 0 && event < BLKDBG_EVENT_MAX);
|
||||
|
||||
injected = false;
|
||||
s->new_state = s->state;
|
||||
QLIST_FOREACH(rule, &s->rules[event], next) {
|
||||
QLIST_FOREACH_SAFE(rule, &s->rules[event], next, next) {
|
||||
injected = process_rule(bs, rule, injected);
|
||||
}
|
||||
s->state = s->new_state;
|
||||
}
|
||||
|
||||
static int blkdebug_debug_breakpoint(BlockDriverState *bs, const char *event,
|
||||
const char *tag)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
struct BlkdebugRule *rule;
|
||||
BlkDebugEvent blkdebug_event;
|
||||
|
||||
if (get_event_by_name(event, &blkdebug_event) < 0) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
rule = g_malloc(sizeof(*rule));
|
||||
*rule = (struct BlkdebugRule) {
|
||||
.event = blkdebug_event,
|
||||
.action = ACTION_SUSPEND,
|
||||
.state = 0,
|
||||
.options.suspend.tag = g_strdup(tag),
|
||||
};
|
||||
|
||||
QLIST_INSERT_HEAD(&s->rules[blkdebug_event], rule, next);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugSuspendedReq *r;
|
||||
|
||||
QLIST_FOREACH(r, &s->suspended_reqs, next) {
|
||||
if (!strcmp(r->tag, tag)) {
|
||||
qemu_coroutine_enter(r->co, NULL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
||||
static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
BlkdebugSuspendedReq *r;
|
||||
|
||||
QLIST_FOREACH(r, &s->suspended_reqs, next) {
|
||||
if (!strcmp(r->tag, tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int64_t blkdebug_getlength(BlockDriverState *bs)
|
||||
{
|
||||
return bdrv_getlength(bs->file);
|
||||
@@ -464,7 +579,10 @@ static BlockDriver bdrv_blkdebug = {
|
||||
.bdrv_aio_readv = blkdebug_aio_readv,
|
||||
.bdrv_aio_writev = blkdebug_aio_writev,
|
||||
|
||||
.bdrv_debug_event = blkdebug_debug_event,
|
||||
.bdrv_debug_event = blkdebug_debug_event,
|
||||
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
|
||||
.bdrv_debug_resume = blkdebug_debug_resume,
|
||||
.bdrv_debug_is_suspended = blkdebug_debug_is_suspended,
|
||||
};
|
||||
|
||||
static void bdrv_blkdebug_init(void)
|
||||
|
||||
+1
-1
@@ -103,7 +103,7 @@ static void coroutine_fn commit_run(void *opaque)
|
||||
|
||||
wait:
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that qemu_aio_flush() returns.
|
||||
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||
*/
|
||||
block_job_sleep_ns(&s->common, rt_clock, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
|
||||
+1
-1
@@ -205,7 +205,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||
}
|
||||
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that qemu_aio_flush() returns.
|
||||
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||
*/
|
||||
block_job_sleep_ns(&s->common, rt_clock, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
|
||||
+114
-77
@@ -615,57 +615,67 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
return cluster_offset;
|
||||
}
|
||||
|
||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i, j = 0, l2_index, ret;
|
||||
uint64_t *old_cluster, start_sect, *l2_table;
|
||||
uint64_t cluster_offset = m->alloc_offset;
|
||||
bool cow = false;
|
||||
int ret;
|
||||
|
||||
trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters);
|
||||
|
||||
if (m->nb_clusters == 0)
|
||||
if (r->nb_sectors == 0) {
|
||||
return 0;
|
||||
|
||||
old_cluster = g_malloc(m->nb_clusters * sizeof(uint64_t));
|
||||
|
||||
/* copy content of unmodified sectors */
|
||||
start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9;
|
||||
if (m->n_start) {
|
||||
cow = true;
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (m->nb_available & (s->cluster_sectors - 1)) {
|
||||
cow = true;
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = copy_sectors(bs, start_sect, cluster_offset, m->nb_available,
|
||||
align_offset(m->nb_available, s->cluster_sectors));
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
ret = copy_sectors(bs, m->offset / BDRV_SECTOR_SIZE, m->alloc_offset,
|
||||
r->offset / BDRV_SECTOR_SIZE,
|
||||
r->offset / BDRV_SECTOR_SIZE + r->nb_sectors);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update L2 table.
|
||||
*
|
||||
* Before we update the L2 table to actually point to the new cluster, we
|
||||
* need to be sure that the refcounts have been increased and COW was
|
||||
* handled.
|
||||
*/
|
||||
if (cow) {
|
||||
qcow2_cache_depends_on_flush(s->l2_table_cache);
|
||||
qcow2_cache_depends_on_flush(s->l2_table_cache);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int i, j = 0, l2_index, ret;
|
||||
uint64_t *old_cluster, *l2_table;
|
||||
uint64_t cluster_offset = m->alloc_offset;
|
||||
|
||||
trace_qcow2_cluster_link_l2(qemu_coroutine_self(), m->nb_clusters);
|
||||
assert(m->nb_clusters > 0);
|
||||
|
||||
old_cluster = g_malloc(m->nb_clusters * sizeof(uint64_t));
|
||||
|
||||
/* copy content of unmodified sectors */
|
||||
ret = perform_cow(bs, m, &m->cow_start);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = perform_cow(bs, m, &m->cow_end);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Update L2 table. */
|
||||
if (s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS) {
|
||||
qcow2_mark_dirty(bs);
|
||||
}
|
||||
if (qcow2_need_accurate_refcounts(s)) {
|
||||
qcow2_cache_set_dependency(bs, s->l2_table_cache,
|
||||
s->refcount_block_cache);
|
||||
}
|
||||
|
||||
ret = get_cluster_table(bs, m->offset, &l2_table, &l2_index);
|
||||
if (ret < 0) {
|
||||
goto err;
|
||||
@@ -743,38 +753,16 @@ out:
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates new clusters for the given guest_offset.
|
||||
*
|
||||
* At most *nb_clusters are allocated, and on return *nb_clusters is updated to
|
||||
* contain the number of clusters that have been allocated and are contiguous
|
||||
* in the image file.
|
||||
*
|
||||
* If *host_offset is non-zero, it specifies the offset in the image file at
|
||||
* which the new clusters must start. *nb_clusters can be 0 on return in this
|
||||
* case if the cluster at host_offset is already in use. If *host_offset is
|
||||
* zero, the clusters can be allocated anywhere in the image file.
|
||||
*
|
||||
* *host_offset is updated to contain the offset into the image file at which
|
||||
* the first allocated cluster starts.
|
||||
*
|
||||
* Return 0 on success and -errno in error cases. -EAGAIN means that the
|
||||
* function has been waiting for another request and the allocation must be
|
||||
* restarted, but the whole request should not be failed.
|
||||
* Check if there already is an AIO write request in flight which allocates
|
||||
* the same cluster. In this case we need to wait until the previous
|
||||
* request has completed and updated the L2 table accordingly.
|
||||
*/
|
||||
static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
uint64_t *host_offset, unsigned int *nb_clusters)
|
||||
static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset,
|
||||
unsigned int *nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
QCowL2Meta *old_alloc;
|
||||
|
||||
trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset,
|
||||
*host_offset, *nb_clusters);
|
||||
|
||||
/*
|
||||
* Check if there already is an AIO write request in flight which allocates
|
||||
* the same cluster. In this case we need to wait until the previous
|
||||
* request has completed and updated the L2 table accordingly.
|
||||
*/
|
||||
QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) {
|
||||
|
||||
uint64_t start = guest_offset >> s->cluster_bits;
|
||||
@@ -807,6 +795,42 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
abort();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocates new clusters for the given guest_offset.
|
||||
*
|
||||
* At most *nb_clusters are allocated, and on return *nb_clusters is updated to
|
||||
* contain the number of clusters that have been allocated and are contiguous
|
||||
* in the image file.
|
||||
*
|
||||
* If *host_offset is non-zero, it specifies the offset in the image file at
|
||||
* which the new clusters must start. *nb_clusters can be 0 on return in this
|
||||
* case if the cluster at host_offset is already in use. If *host_offset is
|
||||
* zero, the clusters can be allocated anywhere in the image file.
|
||||
*
|
||||
* *host_offset is updated to contain the offset into the image file at which
|
||||
* the first allocated cluster starts.
|
||||
*
|
||||
* Return 0 on success and -errno in error cases. -EAGAIN means that the
|
||||
* function has been waiting for another request and the allocation must be
|
||||
* restarted, but the whole request should not be failed.
|
||||
*/
|
||||
static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
uint64_t *host_offset, unsigned int *nb_clusters)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int ret;
|
||||
|
||||
trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset,
|
||||
*host_offset, *nb_clusters);
|
||||
|
||||
ret = handle_dependencies(bs, guest_offset, nb_clusters);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Allocate new clusters */
|
||||
trace_qcow2_cluster_alloc_phys(qemu_coroutine_self());
|
||||
if (*host_offset == 0) {
|
||||
@@ -818,7 +842,7 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
*host_offset = cluster_offset;
|
||||
return 0;
|
||||
} else {
|
||||
int ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters);
|
||||
ret = qcow2_alloc_clusters_at(bs, *host_offset, *nb_clusters);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
@@ -847,7 +871,7 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset,
|
||||
* Return 0 on success and -errno in error cases
|
||||
*/
|
||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int n_start, int n_end, int *num, QCowL2Meta *m)
|
||||
int n_start, int n_end, int *num, uint64_t *host_offset, QCowL2Meta **m)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
int l2_index, ret, sectors;
|
||||
@@ -919,12 +943,6 @@ again:
|
||||
}
|
||||
|
||||
/* If there is something left to allocate, do that now */
|
||||
*m = (QCowL2Meta) {
|
||||
.cluster_offset = cluster_offset,
|
||||
.nb_clusters = 0,
|
||||
};
|
||||
qemu_co_queue_init(&m->dependent_requests);
|
||||
|
||||
if (nb_clusters > 0) {
|
||||
uint64_t alloc_offset;
|
||||
uint64_t alloc_cluster_offset;
|
||||
@@ -957,22 +975,40 @@ again:
|
||||
*
|
||||
* avail_sectors: Number of sectors from the start of the first
|
||||
* newly allocated to the end of the last newly allocated cluster.
|
||||
*
|
||||
* nb_sectors: The number of sectors from the start of the first
|
||||
* newly allocated cluster to the end of the aread that the write
|
||||
* request actually writes to (excluding COW at the end)
|
||||
*/
|
||||
int requested_sectors = n_end - keep_clusters * s->cluster_sectors;
|
||||
int avail_sectors = nb_clusters
|
||||
<< (s->cluster_bits - BDRV_SECTOR_BITS);
|
||||
int alloc_n_start = keep_clusters == 0 ? n_start : 0;
|
||||
int nb_sectors = MIN(requested_sectors, avail_sectors);
|
||||
|
||||
*m = (QCowL2Meta) {
|
||||
.cluster_offset = keep_clusters == 0 ?
|
||||
alloc_cluster_offset : cluster_offset,
|
||||
if (keep_clusters == 0) {
|
||||
cluster_offset = alloc_cluster_offset;
|
||||
}
|
||||
|
||||
*m = g_malloc0(sizeof(**m));
|
||||
|
||||
**m = (QCowL2Meta) {
|
||||
.alloc_offset = alloc_cluster_offset,
|
||||
.offset = alloc_offset,
|
||||
.n_start = keep_clusters == 0 ? n_start : 0,
|
||||
.offset = alloc_offset & ~(s->cluster_size - 1),
|
||||
.nb_clusters = nb_clusters,
|
||||
.nb_available = MIN(requested_sectors, avail_sectors),
|
||||
.nb_available = nb_sectors,
|
||||
|
||||
.cow_start = {
|
||||
.offset = 0,
|
||||
.nb_sectors = alloc_n_start,
|
||||
},
|
||||
.cow_end = {
|
||||
.offset = nb_sectors * BDRV_SECTOR_SIZE,
|
||||
.nb_sectors = avail_sectors - nb_sectors,
|
||||
},
|
||||
};
|
||||
qemu_co_queue_init(&m->dependent_requests);
|
||||
QLIST_INSERT_HEAD(&s->cluster_allocs, m, next_in_flight);
|
||||
qemu_co_queue_init(&(*m)->dependent_requests);
|
||||
QLIST_INSERT_HEAD(&s->cluster_allocs, *m, next_in_flight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -984,12 +1020,13 @@ again:
|
||||
|
||||
assert(sectors > n_start);
|
||||
*num = sectors - n_start;
|
||||
*host_offset = cluster_offset;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (m->nb_clusters > 0) {
|
||||
QLIST_REMOVE(m, next_in_flight);
|
||||
if (*m && (*m)->nb_clusters > 0) {
|
||||
QLIST_REMOVE(*m, next_in_flight);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
+42
-45
@@ -222,7 +222,7 @@ static void report_unsupported_feature(BlockDriverState *bs,
|
||||
* updated successfully. Therefore it is not required to check the return
|
||||
* value of this function.
|
||||
*/
|
||||
static int qcow2_mark_dirty(BlockDriverState *bs)
|
||||
int qcow2_mark_dirty(BlockDriverState *bs)
|
||||
{
|
||||
BDRVQcowState *s = bs->opaque;
|
||||
uint64_t val;
|
||||
@@ -745,21 +745,6 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void run_dependent_requests(BDRVQcowState *s, QCowL2Meta *m)
|
||||
{
|
||||
/* Take the request off the list of running requests */
|
||||
if (m->nb_clusters != 0) {
|
||||
QLIST_REMOVE(m, next_in_flight);
|
||||
}
|
||||
|
||||
/* Restart all dependent requests */
|
||||
if (!qemu_co_queue_empty(&m->dependent_requests)) {
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
qemu_co_queue_restart_all(&m->dependent_requests);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
int64_t sector_num,
|
||||
int remaining_sectors,
|
||||
@@ -774,15 +759,11 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
QEMUIOVector hd_qiov;
|
||||
uint64_t bytes_done = 0;
|
||||
uint8_t *cluster_data = NULL;
|
||||
QCowL2Meta l2meta = {
|
||||
.nb_clusters = 0,
|
||||
};
|
||||
QCowL2Meta *l2meta;
|
||||
|
||||
trace_qcow2_writev_start_req(qemu_coroutine_self(), sector_num,
|
||||
remaining_sectors);
|
||||
|
||||
qemu_co_queue_init(&l2meta.dependent_requests);
|
||||
|
||||
qemu_iovec_init(&hd_qiov, qiov->niov);
|
||||
|
||||
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||
@@ -791,6 +772,8 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
|
||||
while (remaining_sectors != 0) {
|
||||
|
||||
l2meta = NULL;
|
||||
|
||||
trace_qcow2_writev_start_part(qemu_coroutine_self());
|
||||
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||
n_end = index_in_cluster + remaining_sectors;
|
||||
@@ -800,17 +783,11 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
}
|
||||
|
||||
ret = qcow2_alloc_cluster_offset(bs, sector_num << 9,
|
||||
index_in_cluster, n_end, &cur_nr_sectors, &l2meta);
|
||||
index_in_cluster, n_end, &cur_nr_sectors, &cluster_offset, &l2meta);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (l2meta.nb_clusters > 0 &&
|
||||
(s->compatible_features & QCOW2_COMPAT_LAZY_REFCOUNTS)) {
|
||||
qcow2_mark_dirty(bs);
|
||||
}
|
||||
|
||||
cluster_offset = l2meta.cluster_offset;
|
||||
assert((cluster_offset & 511) == 0);
|
||||
|
||||
qemu_iovec_reset(&hd_qiov);
|
||||
@@ -835,8 +812,8 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
cur_nr_sectors * 512);
|
||||
}
|
||||
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||
trace_qcow2_writev_data(qemu_coroutine_self(),
|
||||
(cluster_offset >> 9) + index_in_cluster);
|
||||
ret = bdrv_co_writev(bs->file,
|
||||
@@ -847,12 +824,24 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, &l2meta);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
if (l2meta != NULL) {
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, l2meta);
|
||||
if (ret < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
run_dependent_requests(s, &l2meta);
|
||||
/* Take the request off the list of running requests */
|
||||
if (l2meta->nb_clusters != 0) {
|
||||
QLIST_REMOVE(l2meta, next_in_flight);
|
||||
}
|
||||
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
||||
qemu_co_mutex_lock(&s->lock);
|
||||
|
||||
g_free(l2meta);
|
||||
l2meta = NULL;
|
||||
}
|
||||
|
||||
remaining_sectors -= cur_nr_sectors;
|
||||
sector_num += cur_nr_sectors;
|
||||
@@ -862,10 +851,16 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
|
||||
ret = 0;
|
||||
|
||||
fail:
|
||||
run_dependent_requests(s, &l2meta);
|
||||
|
||||
qemu_co_mutex_unlock(&s->lock);
|
||||
|
||||
if (l2meta != NULL) {
|
||||
if (l2meta->nb_clusters != 0) {
|
||||
QLIST_REMOVE(l2meta, next_in_flight);
|
||||
}
|
||||
qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
||||
g_free(l2meta);
|
||||
}
|
||||
|
||||
qemu_iovec_destroy(&hd_qiov);
|
||||
qemu_vfree(cluster_data);
|
||||
trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
|
||||
@@ -1128,31 +1123,33 @@ static int preallocate(BlockDriverState *bs)
|
||||
{
|
||||
uint64_t nb_sectors;
|
||||
uint64_t offset;
|
||||
uint64_t host_offset = 0;
|
||||
int num;
|
||||
int ret;
|
||||
QCowL2Meta meta;
|
||||
QCowL2Meta *meta;
|
||||
|
||||
nb_sectors = bdrv_getlength(bs) >> 9;
|
||||
offset = 0;
|
||||
qemu_co_queue_init(&meta.dependent_requests);
|
||||
meta.cluster_offset = 0;
|
||||
|
||||
while (nb_sectors) {
|
||||
num = MIN(nb_sectors, INT_MAX >> 9);
|
||||
ret = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num, &meta);
|
||||
ret = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num,
|
||||
&host_offset, &meta);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, &meta);
|
||||
ret = qcow2_alloc_cluster_link_l2(bs, meta);
|
||||
if (ret < 0) {
|
||||
qcow2_free_any_clusters(bs, meta.cluster_offset, meta.nb_clusters);
|
||||
qcow2_free_any_clusters(bs, meta->alloc_offset, meta->nb_clusters);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* There are no dependent requests, but we need to remove our request
|
||||
* from the list of in-flight requests */
|
||||
run_dependent_requests(bs->opaque, &meta);
|
||||
if (meta != NULL) {
|
||||
QLIST_REMOVE(meta, next_in_flight);
|
||||
}
|
||||
|
||||
/* TODO Preallocate data if requested */
|
||||
|
||||
@@ -1165,10 +1162,10 @@ static int preallocate(BlockDriverState *bs)
|
||||
* all of the allocated clusters (otherwise we get failing reads after
|
||||
* EOF). Extend the image to the last allocated sector.
|
||||
*/
|
||||
if (meta.cluster_offset != 0) {
|
||||
if (host_offset != 0) {
|
||||
uint8_t buf[512];
|
||||
memset(buf, 0, 512);
|
||||
ret = bdrv_write(bs->file, (meta.cluster_offset >> 9) + num - 1, buf, 1);
|
||||
ret = bdrv_write(bs->file, (host_offset >> 9) + num - 1, buf, 1);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
+45
-4
@@ -196,17 +196,56 @@ typedef struct QCowCreateState {
|
||||
|
||||
struct QCowAIOCB;
|
||||
|
||||
/* XXX This could be private for qcow2-cluster.c */
|
||||
typedef struct Qcow2COWRegion {
|
||||
/**
|
||||
* Offset of the COW region in bytes from the start of the first cluster
|
||||
* touched by the request.
|
||||
*/
|
||||
uint64_t offset;
|
||||
|
||||
/** Number of sectors to copy */
|
||||
int nb_sectors;
|
||||
} Qcow2COWRegion;
|
||||
|
||||
/**
|
||||
* Describes an in-flight (part of a) write request that writes to clusters
|
||||
* that are not referenced in their L2 table yet.
|
||||
*/
|
||||
typedef struct QCowL2Meta
|
||||
{
|
||||
/** Guest offset of the first newly allocated cluster */
|
||||
uint64_t offset;
|
||||
uint64_t cluster_offset;
|
||||
|
||||
/** Host offset of the first newly allocated cluster */
|
||||
uint64_t alloc_offset;
|
||||
int n_start;
|
||||
|
||||
/**
|
||||
* Number of sectors from the start of the first allocated cluster to
|
||||
* the end of the (possibly shortened) request
|
||||
*/
|
||||
int nb_available;
|
||||
|
||||
/** Number of newly allocated clusters */
|
||||
int nb_clusters;
|
||||
|
||||
/**
|
||||
* Requests that overlap with this allocation and wait to be restarted
|
||||
* when the allocating request has completed.
|
||||
*/
|
||||
CoQueue dependent_requests;
|
||||
|
||||
/**
|
||||
* The COW Region between the start of the first allocated cluster and the
|
||||
* area the guest actually writes to.
|
||||
*/
|
||||
Qcow2COWRegion cow_start;
|
||||
|
||||
/**
|
||||
* The COW Region between the area the guest actually writes to and the
|
||||
* end of the last allocated cluster.
|
||||
*/
|
||||
Qcow2COWRegion cow_end;
|
||||
|
||||
QLIST_ENTRY(QCowL2Meta) next_in_flight;
|
||||
} QCowL2Meta;
|
||||
|
||||
@@ -264,6 +303,8 @@ static inline bool qcow2_need_accurate_refcounts(BDRVQcowState *s)
|
||||
/* qcow2.c functions */
|
||||
int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
|
||||
int64_t sector_num, int nb_sectors);
|
||||
|
||||
int qcow2_mark_dirty(BlockDriverState *bs);
|
||||
int qcow2_update_header(BlockDriverState *bs);
|
||||
|
||||
/* qcow2-refcount.c functions */
|
||||
@@ -297,7 +338,7 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
|
||||
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int *num, uint64_t *cluster_offset);
|
||||
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
int n_start, int n_end, int *num, QCowL2Meta *m);
|
||||
int n_start, int n_end, int *num, uint64_t *host_offset, QCowL2Meta **m);
|
||||
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||
uint64_t offset,
|
||||
int compressed_size);
|
||||
|
||||
+10
-17
@@ -708,22 +708,6 @@ static BlockDriverAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
||||
return thread_pool_submit_aio(aio_worker, acb, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *paio_ioctl(BlockDriverState *bs, int fd,
|
||||
unsigned long int req, void *buf,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
RawPosixAIOData *acb = g_slice_new(RawPosixAIOData);
|
||||
|
||||
acb->bs = bs;
|
||||
acb->aio_type = QEMU_AIO_IOCTL;
|
||||
acb->aio_fildes = fd;
|
||||
acb->aio_offset = 0;
|
||||
acb->aio_ioctl_buf = buf;
|
||||
acb->aio_ioctl_cmd = req;
|
||||
|
||||
return thread_pool_submit_aio(aio_worker, acb, cb, opaque);
|
||||
}
|
||||
|
||||
static BlockDriverAIOCB *raw_aio_submit(BlockDriverState *bs,
|
||||
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
|
||||
BlockDriverCompletionFunc *cb, void *opaque, int type)
|
||||
@@ -1346,10 +1330,19 @@ static BlockDriverAIOCB *hdev_aio_ioctl(BlockDriverState *bs,
|
||||
BlockDriverCompletionFunc *cb, void *opaque)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
RawPosixAIOData *acb;
|
||||
|
||||
if (fd_open(bs) < 0)
|
||||
return NULL;
|
||||
return paio_ioctl(bs, s->fd, req, buf, cb, opaque);
|
||||
|
||||
acb = g_slice_new(RawPosixAIOData);
|
||||
acb->bs = bs;
|
||||
acb->aio_type = QEMU_AIO_IOCTL;
|
||||
acb->aio_fildes = s->fd;
|
||||
acb->aio_offset = 0;
|
||||
acb->aio_ioctl_buf = buf;
|
||||
acb->aio_ioctl_cmd = req;
|
||||
return thread_pool_submit_aio(aio_worker, acb, cb, opaque);
|
||||
}
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||
|
||||
+14
-3
@@ -303,13 +303,24 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset)
|
||||
{
|
||||
BDRVRawState *s = bs->opaque;
|
||||
LONG low, high;
|
||||
DWORD dwPtrLow;
|
||||
|
||||
low = offset;
|
||||
high = offset >> 32;
|
||||
if (!SetFilePointer(s->hfile, low, &high, FILE_BEGIN))
|
||||
return -EIO;
|
||||
if (!SetEndOfFile(s->hfile))
|
||||
|
||||
/*
|
||||
* An error has occurred if the return value is INVALID_SET_FILE_POINTER
|
||||
* and GetLastError doesn't return NO_ERROR.
|
||||
*/
|
||||
dwPtrLow = SetFilePointer(s->hfile, low, &high, FILE_BEGIN);
|
||||
if (dwPtrLow == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
|
||||
fprintf(stderr, "SetFilePointer error: %d\n", GetLastError());
|
||||
return -EIO;
|
||||
}
|
||||
if (SetEndOfFile(s->hfile) == 0) {
|
||||
fprintf(stderr, "SetEndOfFile error: %d\n", GetLastError());
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+12
-8
@@ -77,6 +77,7 @@ typedef struct RBDAIOCB {
|
||||
int error;
|
||||
struct BDRVRBDState *s;
|
||||
int cancelled;
|
||||
int status;
|
||||
} RBDAIOCB;
|
||||
|
||||
typedef struct RADOSCB {
|
||||
@@ -376,12 +377,6 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
||||
RBDAIOCB *acb = rcb->acb;
|
||||
int64_t r;
|
||||
|
||||
if (acb->cancelled) {
|
||||
qemu_vfree(acb->bounce);
|
||||
qemu_aio_release(acb);
|
||||
goto done;
|
||||
}
|
||||
|
||||
r = rcb->ret;
|
||||
|
||||
if (acb->cmd == RBD_AIO_WRITE ||
|
||||
@@ -409,7 +404,6 @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
|
||||
/* Note that acb->bh can be NULL in case where the aio was cancelled */
|
||||
acb->bh = qemu_bh_new(rbd_aio_bh_cb, acb);
|
||||
qemu_bh_schedule(acb->bh);
|
||||
done:
|
||||
g_free(rcb);
|
||||
}
|
||||
|
||||
@@ -568,6 +562,12 @@ static void qemu_rbd_aio_cancel(BlockDriverAIOCB *blockacb)
|
||||
{
|
||||
RBDAIOCB *acb = (RBDAIOCB *) blockacb;
|
||||
acb->cancelled = 1;
|
||||
|
||||
while (acb->status == -EINPROGRESS) {
|
||||
qemu_aio_wait();
|
||||
}
|
||||
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
|
||||
static const AIOCBInfo rbd_aiocb_info = {
|
||||
@@ -639,8 +639,11 @@ static void rbd_aio_bh_cb(void *opaque)
|
||||
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->bh = NULL;
|
||||
acb->status = 0;
|
||||
|
||||
qemu_aio_release(acb);
|
||||
if (!acb->cancelled) {
|
||||
qemu_aio_release(acb);
|
||||
}
|
||||
}
|
||||
|
||||
static int rbd_aio_discard_wrapper(rbd_image_t image,
|
||||
@@ -685,6 +688,7 @@ static BlockDriverAIOCB *rbd_start_aio(BlockDriverState *bs,
|
||||
acb->s = s;
|
||||
acb->cancelled = 0;
|
||||
acb->bh = NULL;
|
||||
acb->status = -EINPROGRESS;
|
||||
|
||||
if (cmd == RBD_AIO_WRITE) {
|
||||
qemu_iovec_to_buf(acb->qiov, 0, acb->bounce, qiov->size);
|
||||
|
||||
+1
-1
@@ -108,7 +108,7 @@ static void coroutine_fn stream_run(void *opaque)
|
||||
|
||||
wait:
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that qemu_aio_flush() returns.
|
||||
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||
*/
|
||||
block_job_sleep_ns(&s->common, rt_clock, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
|
||||
+19
-5
@@ -26,6 +26,9 @@
|
||||
#include "block_int.h"
|
||||
#include "module.h"
|
||||
#include "migration.h"
|
||||
#if defined(CONFIG_UUID)
|
||||
#include <uuid/uuid.h>
|
||||
#endif
|
||||
|
||||
/**************************************************************/
|
||||
|
||||
@@ -198,7 +201,8 @@ static int vpc_open(BlockDriverState *bs, int flags)
|
||||
bs->total_sectors = (int64_t)
|
||||
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
|
||||
|
||||
if (bs->total_sectors >= 65535 * 16 * 255) {
|
||||
/* Allow a maximum disk size of approximately 2 TB */
|
||||
if (bs->total_sectors >= 65535LL * 255 * 255) {
|
||||
err = -EFBIG;
|
||||
goto fail;
|
||||
}
|
||||
@@ -524,19 +528,27 @@ static coroutine_fn int vpc_co_write(BlockDriverState *bs, int64_t sector_num,
|
||||
* Note that the geometry doesn't always exactly match total_sectors but
|
||||
* may round it down.
|
||||
*
|
||||
* Returns 0 on success, -EFBIG if the size is larger than 127 GB
|
||||
* Returns 0 on success, -EFBIG if the size is larger than ~2 TB. Override
|
||||
* the hardware EIDE and ATA-2 limit of 16 heads (max disk size of 127 GB)
|
||||
* and instead allow up to 255 heads.
|
||||
*/
|
||||
static int calculate_geometry(int64_t total_sectors, uint16_t* cyls,
|
||||
uint8_t* heads, uint8_t* secs_per_cyl)
|
||||
{
|
||||
uint32_t cyls_times_heads;
|
||||
|
||||
if (total_sectors > 65535 * 16 * 255)
|
||||
/* Allow a maximum disk size of approximately 2 TB */
|
||||
if (total_sectors > 65535LL * 255 * 255) {
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
if (total_sectors > 65535 * 16 * 63) {
|
||||
*secs_per_cyl = 255;
|
||||
*heads = 16;
|
||||
if (total_sectors > 65535 * 16 * 255) {
|
||||
*heads = 255;
|
||||
} else {
|
||||
*heads = 16;
|
||||
}
|
||||
cyls_times_heads = total_sectors / *secs_per_cyl;
|
||||
} else {
|
||||
*secs_per_cyl = 17;
|
||||
@@ -739,7 +751,9 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options)
|
||||
|
||||
footer->type = be32_to_cpu(disk_type);
|
||||
|
||||
/* TODO uuid is missing */
|
||||
#if defined(CONFIG_UUID)
|
||||
uuid_generate(footer->uuid);
|
||||
#endif
|
||||
|
||||
footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE));
|
||||
|
||||
|
||||
@@ -190,6 +190,12 @@ struct BlockDriver {
|
||||
|
||||
void (*bdrv_debug_event)(BlockDriverState *bs, BlkDebugEvent event);
|
||||
|
||||
/* TODO Better pass a option string/QDict/QemuOpts to add any rule? */
|
||||
int (*bdrv_debug_breakpoint)(BlockDriverState *bs, const char *event,
|
||||
const char *tag);
|
||||
int (*bdrv_debug_resume)(BlockDriverState *bs, const char *tag);
|
||||
bool (*bdrv_debug_is_suspended)(BlockDriverState *bs, const char *tag);
|
||||
|
||||
/*
|
||||
* Returns 1 if newly created images are guaranteed to contain only
|
||||
* zeros, 0 otherwise.
|
||||
|
||||
+18
-17
@@ -275,7 +275,7 @@ static bool do_check_io_limits(BlockIOLimit *io_limits)
|
||||
return true;
|
||||
}
|
||||
|
||||
DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
DriveInfo *drive_init(QemuOpts *opts, BlockInterfaceType block_default_type)
|
||||
{
|
||||
const char *buf;
|
||||
const char *file = NULL;
|
||||
@@ -325,7 +325,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
type = default_to_scsi ? IF_SCSI : IF_IDE;
|
||||
type = block_default_type;
|
||||
}
|
||||
|
||||
max_devs = if_max_devs[type];
|
||||
@@ -568,7 +568,7 @@ DriveInfo *drive_init(QemuOpts *opts, int default_to_scsi)
|
||||
break;
|
||||
case IF_VIRTIO:
|
||||
/* add virtio block device */
|
||||
opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, NULL);
|
||||
opts = qemu_opts_create_nofail(qemu_find_opts("device"));
|
||||
if (arch_type == QEMU_ARCH_S390X) {
|
||||
qemu_opt_set(opts, "driver", "virtio-blk-s390");
|
||||
} else {
|
||||
@@ -707,6 +707,7 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
|
||||
int ret = 0;
|
||||
BlockdevActionList *dev_entry = dev_list;
|
||||
BlkTransactionStates *states, *next;
|
||||
Error *local_err = NULL;
|
||||
|
||||
QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionStates) snap_bdrv_states;
|
||||
QSIMPLEQ_INIT(&snap_bdrv_states);
|
||||
@@ -786,12 +787,12 @@ void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
|
||||
|
||||
/* create new image w/backing file */
|
||||
if (mode != NEW_IMAGE_MODE_EXISTING) {
|
||||
ret = bdrv_img_create(new_image_file, format,
|
||||
states->old_bs->filename,
|
||||
states->old_bs->drv->format_name,
|
||||
NULL, -1, flags);
|
||||
if (ret) {
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file);
|
||||
bdrv_img_create(new_image_file, format,
|
||||
states->old_bs->filename,
|
||||
states->old_bs->drv->format_name,
|
||||
NULL, -1, flags, &local_err);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
goto delete_and_fail;
|
||||
}
|
||||
}
|
||||
@@ -1263,8 +1264,8 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||
assert(format && drv);
|
||||
bdrv_get_geometry(bs, &size);
|
||||
size *= 512;
|
||||
ret = bdrv_img_create(target, format,
|
||||
NULL, NULL, NULL, size, flags);
|
||||
bdrv_img_create(target, format,
|
||||
NULL, NULL, NULL, size, flags, &local_err);
|
||||
} else {
|
||||
switch (mode) {
|
||||
case NEW_IMAGE_MODE_EXISTING:
|
||||
@@ -1272,18 +1273,18 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||
break;
|
||||
case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
|
||||
/* create new image with backing file */
|
||||
ret = bdrv_img_create(target, format,
|
||||
source->filename,
|
||||
source->drv->format_name,
|
||||
NULL, -1, flags);
|
||||
bdrv_img_create(target, format,
|
||||
source->filename,
|
||||
source->drv->format_name,
|
||||
NULL, -1, flags, &local_err);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
error_set(errp, QERR_OPEN_FILE_FAILED, target);
|
||||
if (error_is_set(&local_err)) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+7
-2
@@ -19,8 +19,13 @@ void blockdev_auto_del(BlockDriverState *bs);
|
||||
|
||||
typedef enum {
|
||||
IF_DEFAULT = -1, /* for use with drive_add() only */
|
||||
/*
|
||||
* IF_IDE must be zero, because we want QEMUMachine member
|
||||
* block_default_type to default-initialize to IF_IDE
|
||||
*/
|
||||
IF_IDE = 0,
|
||||
IF_NONE,
|
||||
IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN,
|
||||
IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO, IF_XEN,
|
||||
IF_COUNT
|
||||
} BlockInterfaceType;
|
||||
|
||||
@@ -51,7 +56,7 @@ DriveInfo *drive_get_by_blockdev(BlockDriverState *bs);
|
||||
QemuOpts *drive_def(const char *optstr);
|
||||
QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file,
|
||||
const char *optstr);
|
||||
DriveInfo *drive_init(QemuOpts *arg, int default_to_scsi);
|
||||
DriveInfo *drive_init(QemuOpts *arg, BlockInterfaceType block_default_type);
|
||||
|
||||
/* device-hotplug */
|
||||
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@
|
||||
#ifndef HW_BOARDS_H
|
||||
#define HW_BOARDS_H
|
||||
|
||||
#include "blockdev.h"
|
||||
#include "qdev.h"
|
||||
|
||||
typedef struct QEMUMachineInitArgs {
|
||||
@@ -24,7 +25,7 @@ typedef struct QEMUMachine {
|
||||
const char *desc;
|
||||
QEMUMachineInitFunc *init;
|
||||
QEMUMachineResetFunc *reset;
|
||||
int use_scsi;
|
||||
BlockInterfaceType block_default_type;
|
||||
int max_cpus;
|
||||
unsigned int no_serial:1,
|
||||
no_parallel:1,
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ DriveInfo *add_init_drive(const char *optstr)
|
||||
if (!opts)
|
||||
return NULL;
|
||||
|
||||
dinfo = drive_init(opts, current_machine->use_scsi);
|
||||
dinfo = drive_init(opts, current_machine->block_default_type);
|
||||
if (!dinfo) {
|
||||
qemu_opts_del(opts);
|
||||
return NULL;
|
||||
|
||||
+1
-1
@@ -329,7 +329,7 @@ static QEMUMachine highbank_machine = {
|
||||
.name = "highbank",
|
||||
.desc = "Calxeda Highbank (ECX-1000)",
|
||||
.init = highbank_init,
|
||||
.use_scsi = 1,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = 4,
|
||||
};
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user