Block layer patches
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJXhioJAAoJEH8JsnLIjy/WofoQAJWaZAZclaWWWDsM5i13JMZV pm+5t6wTmSdflDbwk3htJHz1p169hTYytB6c/RN/HiiAq42jy/axbZnJoHp/Oeyg kJwyNl0A6nq6wUq9O6r+rns0V7+n78eviGHnFwhAyDx0bxIKDURV3hI87TRePNlR V3c//m7PHnqlA/ebg1kdA0L4v+CMTNwD+uG+/tfAZJSwi6KSNMs3jU0lXZ6+OLos dJ4TZpS1U/sQdyAp7dEZvhjUSHXAKTNY2KIam2dx33rTqrhsEONPrD+hCDzAPm4S po5OArTMikXA+0KPI/xtCoH1CS+yqAGqfGpbqQg0bAhCiUQ4/Fq/RjPZ6+84476O M+WuJTmocd7v50HPFTENsVmssFu3R9sm5azujcoivnxn69EXRZg26ahPPbsdmN5m uAjJItc7iWWNy1/Z6FPantZ3Ptr4Pwf/+StHaTOCBCejfldKg3rp0UxZO0GJa33T 2BIO06JBvOzLZN3bMKVsgKyHHmLSvLEV5Dg+ZUtcDblRG873MCnKrxq79Z6/ykco 7lAcEqel8Opbn9yhW1l06ozT+Ur7NuWBKFV9hunkvFzrBDu0KGPr4ragiFNDYGKV iaQqbpyqAjUqt5H9Tj+Z4Gv4ngdxDgoxJPT5Y7Tzcz2fz7Hnqj1nYFaUy41B9hPi U+aaqh4sK1CSxOuX4v39 =null -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging Block layer patches # gpg: Signature made Wed 13 Jul 2016 12:46:17 BST # gpg: using RSA key 0x7F09B272C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * remotes/kevin/tags/for-upstream: (34 commits) iotests: Make 157 actually format-agnostic vvfat: Fix qcow write target driver specification hmp: show all of snapshot info on every block dev in output of 'info snapshots' hmp: use snapshot name to determine whether a snapshot is 'fully available' qemu-iotests: Test naming of throttling groups blockdev: Fix regression with the default naming of throttling groups vmdk: fix metadata write regression Improve block job rate limiting for small bandwidth values qcow2: Fix qcow2_get_cluster_offset() qemu-io: Use correct range limitations qcow2: Avoid making the L1 table too big qemu-img: Use strerror() for generic resize error block: Remove BB options from blockdev-add qemu-iotests: Test setting WCE with qdev block/qdev: Allow configuring rerror/werror with qdev properties commit: Fix use of error handling policy block/qdev: Allow configuring WCE with qdev properties block/qdev: Allow node name for drive properties coroutine: move entry argument to qemu_coroutine_create test-coroutine: prepare for the next patch ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
9358450e98
4
block.c
4
block.c
@ -329,8 +329,8 @@ int bdrv_create(BlockDriver *drv, const char* filename,
|
||||
/* Fast-path if already in coroutine context */
|
||||
bdrv_create_co_entry(&cco);
|
||||
} else {
|
||||
co = qemu_coroutine_create(bdrv_create_co_entry);
|
||||
qemu_coroutine_enter(co, &cco);
|
||||
co = qemu_coroutine_create(bdrv_create_co_entry, &cco);
|
||||
qemu_coroutine_enter(co);
|
||||
while (cco.ret == NOT_DONE) {
|
||||
aio_poll(qemu_get_aio_context(), true);
|
||||
}
|
||||
|
@ -474,9 +474,9 @@ static void coroutine_fn backup_run(void *opaque)
|
||||
block_job_defer_to_main_loop(&job->common, backup_complete, data);
|
||||
}
|
||||
|
||||
void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
int64_t speed, MirrorSyncMode sync_mode,
|
||||
BdrvDirtyBitmap *sync_bitmap,
|
||||
void backup_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *target, int64_t speed,
|
||||
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
BlockCompletionFunc *cb, void *opaque,
|
||||
@ -541,7 +541,8 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
goto error;
|
||||
}
|
||||
|
||||
job = block_job_create(&backup_job_driver, bs, speed, cb, opaque, errp);
|
||||
job = block_job_create(job_id, &backup_job_driver, bs, speed,
|
||||
cb, opaque, errp);
|
||||
if (!job) {
|
||||
goto error;
|
||||
}
|
||||
@ -575,9 +576,9 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
|
||||
bdrv_op_block_all(target, job->common.blocker);
|
||||
job->common.len = len;
|
||||
job->common.co = qemu_coroutine_create(backup_run);
|
||||
job->common.co = qemu_coroutine_create(backup_run, job);
|
||||
block_job_txn_add_job(txn, &job->common);
|
||||
qemu_coroutine_enter(job->common.co, job);
|
||||
qemu_coroutine_enter(job->common.co);
|
||||
return;
|
||||
|
||||
error:
|
||||
|
@ -621,7 +621,7 @@ static int blkdebug_debug_resume(BlockDriverState *bs, const char *tag)
|
||||
|
||||
QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, next) {
|
||||
if (!strcmp(r->tag, tag)) {
|
||||
qemu_coroutine_enter(r->co, NULL);
|
||||
qemu_coroutine_enter(r->co);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -647,7 +647,7 @@ static int blkdebug_debug_remove_breakpoint(BlockDriverState *bs,
|
||||
}
|
||||
QLIST_FOREACH_SAFE(r, &s->suspended_reqs, next, r_next) {
|
||||
if (!strcmp(r->tag, tag)) {
|
||||
qemu_coroutine_enter(r->co, NULL);
|
||||
qemu_coroutine_enter(r->co);
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ static int64_t blkreplay_getlength(BlockDriverState *bs)
|
||||
static void blkreplay_bh_cb(void *opaque)
|
||||
{
|
||||
Request *req = opaque;
|
||||
qemu_coroutine_enter(req->co, NULL);
|
||||
qemu_coroutine_enter(req->co);
|
||||
qemu_bh_delete(req->bh);
|
||||
g_free(req);
|
||||
}
|
||||
|
@ -836,8 +836,8 @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
|
||||
.ret = NOT_DONE,
|
||||
};
|
||||
|
||||
co = qemu_coroutine_create(co_entry);
|
||||
qemu_coroutine_enter(co, &rwco);
|
||||
co = qemu_coroutine_create(co_entry, &rwco);
|
||||
qemu_coroutine_enter(co);
|
||||
|
||||
aio_context = blk_get_aio_context(blk);
|
||||
while (rwco.ret == NOT_DONE) {
|
||||
@ -950,8 +950,8 @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes,
|
||||
acb->bh = NULL;
|
||||
acb->has_returned = false;
|
||||
|
||||
co = qemu_coroutine_create(co_entry);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
co = qemu_coroutine_create(co_entry, acb);
|
||||
qemu_coroutine_enter(co);
|
||||
|
||||
acb->has_returned = true;
|
||||
if (acb->rwco.ret != NOT_DONE) {
|
||||
@ -1173,6 +1173,7 @@ BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read,
|
||||
return BLOCK_ERROR_ACTION_REPORT;
|
||||
case BLOCKDEV_ON_ERROR_IGNORE:
|
||||
return BLOCK_ERROR_ACTION_IGNORE;
|
||||
case BLOCKDEV_ON_ERROR_AUTO:
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
@ -113,6 +113,7 @@ static void coroutine_fn commit_run(void *opaque)
|
||||
CommitBlockJob *s = opaque;
|
||||
CommitCompleteData *data;
|
||||
int64_t sector_num, end;
|
||||
uint64_t delay_ns = 0;
|
||||
int ret = 0;
|
||||
int n = 0;
|
||||
void *buf = NULL;
|
||||
@ -142,10 +143,8 @@ static void coroutine_fn commit_run(void *opaque)
|
||||
buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE);
|
||||
|
||||
for (sector_num = 0; sector_num < end; sector_num += n) {
|
||||
uint64_t delay_ns = 0;
|
||||
bool copy;
|
||||
|
||||
wait:
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||
*/
|
||||
@ -161,19 +160,13 @@ wait:
|
||||
copy = (ret == 1);
|
||||
trace_commit_one_iteration(s, sector_num, n, ret);
|
||||
if (copy) {
|
||||
if (s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
if (delay_ns > 0) {
|
||||
goto wait;
|
||||
}
|
||||
}
|
||||
ret = commit_populate(s->top, s->base, sector_num, n, buf);
|
||||
bytes_written += n * BDRV_SECTOR_SIZE;
|
||||
}
|
||||
if (ret < 0) {
|
||||
if (s->on_error == BLOCKDEV_ON_ERROR_STOP ||
|
||||
s->on_error == BLOCKDEV_ON_ERROR_REPORT||
|
||||
(s->on_error == BLOCKDEV_ON_ERROR_ENOSPC && ret == -ENOSPC)) {
|
||||
BlockErrorAction action =
|
||||
block_job_error_action(&s->common, false, s->on_error, -ret);
|
||||
if (action == BLOCK_ERROR_ACTION_REPORT) {
|
||||
goto out;
|
||||
} else {
|
||||
n = 0;
|
||||
@ -182,6 +175,10 @@ wait:
|
||||
}
|
||||
/* Publish progress */
|
||||
s->common.offset += n * BDRV_SECTOR_SIZE;
|
||||
|
||||
if (copy && s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
}
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
@ -211,8 +208,8 @@ static const BlockJobDriver commit_job_driver = {
|
||||
.set_speed = commit_set_speed,
|
||||
};
|
||||
|
||||
void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
BlockDriverState *top, int64_t speed,
|
||||
void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *base, BlockDriverState *top, int64_t speed,
|
||||
BlockdevOnError on_error, BlockCompletionFunc *cb,
|
||||
void *opaque, const char *backing_file_str, Error **errp)
|
||||
{
|
||||
@ -236,7 +233,8 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
return;
|
||||
}
|
||||
|
||||
s = block_job_create(&commit_job_driver, bs, speed, cb, opaque, errp);
|
||||
s = block_job_create(job_id, &commit_job_driver, bs, speed,
|
||||
cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
@ -277,10 +275,10 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
s->backing_file_str = g_strdup(backing_file_str);
|
||||
|
||||
s->on_error = on_error;
|
||||
s->common.co = qemu_coroutine_create(commit_run);
|
||||
s->common.co = qemu_coroutine_create(commit_run, s);
|
||||
|
||||
trace_commit_start(bs, base, top, s, s->common.co, opaque);
|
||||
qemu_coroutine_enter(s->common.co, s);
|
||||
qemu_coroutine_enter(s->common.co);
|
||||
}
|
||||
|
||||
|
||||
|
@ -233,7 +233,7 @@ static void qemu_gluster_complete_aio(void *opaque)
|
||||
|
||||
qemu_bh_delete(acb->bh);
|
||||
acb->bh = NULL;
|
||||
qemu_coroutine_enter(acb->coroutine, NULL);
|
||||
qemu_coroutine_enter(acb->coroutine);
|
||||
}
|
||||
|
||||
/*
|
||||
|
45
block/io.c
45
block/io.c
@ -195,7 +195,7 @@ static void bdrv_co_drain_bh_cb(void *opaque)
|
||||
qemu_bh_delete(data->bh);
|
||||
bdrv_drain_poll(data->bs);
|
||||
data->done = true;
|
||||
qemu_coroutine_enter(co, NULL);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
|
||||
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs)
|
||||
@ -599,8 +599,8 @@ static int bdrv_prwv_co(BdrvChild *child, int64_t offset,
|
||||
} else {
|
||||
AioContext *aio_context = bdrv_get_aio_context(child->bs);
|
||||
|
||||
co = qemu_coroutine_create(bdrv_rw_co_entry);
|
||||
qemu_coroutine_enter(co, &rwco);
|
||||
co = qemu_coroutine_create(bdrv_rw_co_entry, &rwco);
|
||||
qemu_coroutine_enter(co);
|
||||
while (rwco.ret == NOT_DONE) {
|
||||
aio_poll(aio_context, true);
|
||||
}
|
||||
@ -799,7 +799,7 @@ static void bdrv_co_io_em_complete(void *opaque, int ret)
|
||||
CoroutineIOCompletion *co = opaque;
|
||||
|
||||
co->ret = ret;
|
||||
qemu_coroutine_enter(co->coroutine, NULL);
|
||||
qemu_coroutine_enter(co->coroutine);
|
||||
}
|
||||
|
||||
static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs,
|
||||
@ -1752,8 +1752,9 @@ int64_t bdrv_get_block_status_above(BlockDriverState *bs,
|
||||
} else {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
co = qemu_coroutine_create(bdrv_get_block_status_above_co_entry);
|
||||
qemu_coroutine_enter(co, &data);
|
||||
co = qemu_coroutine_create(bdrv_get_block_status_above_co_entry,
|
||||
&data);
|
||||
qemu_coroutine_enter(co);
|
||||
while (!data.done) {
|
||||
aio_poll(aio_context, true);
|
||||
}
|
||||
@ -1901,9 +1902,9 @@ bdrv_rw_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos,
|
||||
.is_read = is_read,
|
||||
.ret = -EINPROGRESS,
|
||||
};
|
||||
Coroutine *co = qemu_coroutine_create(bdrv_co_rw_vmstate_entry);
|
||||
Coroutine *co = qemu_coroutine_create(bdrv_co_rw_vmstate_entry, &data);
|
||||
|
||||
qemu_coroutine_enter(co, &data);
|
||||
qemu_coroutine_enter(co);
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
}
|
||||
@ -2113,8 +2114,8 @@ static BlockAIOCB *bdrv_co_aio_rw_vector(BdrvChild *child,
|
||||
acb->req.flags = flags;
|
||||
acb->is_write = is_write;
|
||||
|
||||
co = qemu_coroutine_create(bdrv_co_do_rw);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
co = qemu_coroutine_create(bdrv_co_do_rw, acb);
|
||||
qemu_coroutine_enter(co);
|
||||
|
||||
bdrv_co_maybe_schedule_bh(acb);
|
||||
return &acb->common;
|
||||
@ -2141,8 +2142,8 @@ BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs,
|
||||
acb->need_bh = true;
|
||||
acb->req.error = -EINPROGRESS;
|
||||
|
||||
co = qemu_coroutine_create(bdrv_aio_flush_co_entry);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
co = qemu_coroutine_create(bdrv_aio_flush_co_entry, acb);
|
||||
qemu_coroutine_enter(co);
|
||||
|
||||
bdrv_co_maybe_schedule_bh(acb);
|
||||
return &acb->common;
|
||||
@ -2171,8 +2172,8 @@ BlockAIOCB *bdrv_aio_discard(BlockDriverState *bs,
|
||||
acb->req.error = -EINPROGRESS;
|
||||
acb->req.sector = sector_num;
|
||||
acb->req.nb_sectors = nb_sectors;
|
||||
co = qemu_coroutine_create(bdrv_aio_discard_co_entry);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
co = qemu_coroutine_create(bdrv_aio_discard_co_entry, acb);
|
||||
qemu_coroutine_enter(co);
|
||||
|
||||
bdrv_co_maybe_schedule_bh(acb);
|
||||
return &acb->common;
|
||||
@ -2313,8 +2314,8 @@ int bdrv_flush(BlockDriverState *bs)
|
||||
} else {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
co = qemu_coroutine_create(bdrv_flush_co_entry);
|
||||
qemu_coroutine_enter(co, &flush_co);
|
||||
co = qemu_coroutine_create(bdrv_flush_co_entry, &flush_co);
|
||||
qemu_coroutine_enter(co);
|
||||
while (flush_co.ret == NOT_DONE) {
|
||||
aio_poll(aio_context, true);
|
||||
}
|
||||
@ -2442,8 +2443,8 @@ int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors)
|
||||
} else {
|
||||
AioContext *aio_context = bdrv_get_aio_context(bs);
|
||||
|
||||
co = qemu_coroutine_create(bdrv_discard_co_entry);
|
||||
qemu_coroutine_enter(co, &rwco);
|
||||
co = qemu_coroutine_create(bdrv_discard_co_entry, &rwco);
|
||||
qemu_coroutine_enter(co);
|
||||
while (rwco.ret == NOT_DONE) {
|
||||
aio_poll(aio_context, true);
|
||||
}
|
||||
@ -2505,9 +2506,9 @@ int bdrv_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
|
||||
/* Fast-path if already in coroutine context */
|
||||
bdrv_co_ioctl_entry(&data);
|
||||
} else {
|
||||
Coroutine *co = qemu_coroutine_create(bdrv_co_ioctl_entry);
|
||||
Coroutine *co = qemu_coroutine_create(bdrv_co_ioctl_entry, &data);
|
||||
|
||||
qemu_coroutine_enter(co, &data);
|
||||
qemu_coroutine_enter(co);
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(bdrv_get_aio_context(bs), true);
|
||||
}
|
||||
@ -2535,8 +2536,8 @@ BlockAIOCB *bdrv_aio_ioctl(BlockDriverState *bs,
|
||||
acb->req.error = -EINPROGRESS;
|
||||
acb->req.req = req;
|
||||
acb->req.buf = buf;
|
||||
co = qemu_coroutine_create(bdrv_co_aio_ioctl_entry);
|
||||
qemu_coroutine_enter(co, acb);
|
||||
co = qemu_coroutine_create(bdrv_co_aio_ioctl_entry, acb);
|
||||
qemu_coroutine_enter(co);
|
||||
|
||||
bdrv_co_maybe_schedule_bh(acb);
|
||||
return &acb->common;
|
||||
|
@ -152,7 +152,7 @@ static void iscsi_co_generic_bh_cb(void *opaque)
|
||||
struct IscsiTask *iTask = opaque;
|
||||
iTask->complete = 1;
|
||||
qemu_bh_delete(iTask->bh);
|
||||
qemu_coroutine_enter(iTask->co, NULL);
|
||||
qemu_coroutine_enter(iTask->co);
|
||||
}
|
||||
|
||||
static void iscsi_retry_timer_expired(void *opaque)
|
||||
@ -160,7 +160,7 @@ static void iscsi_retry_timer_expired(void *opaque)
|
||||
struct IscsiTask *iTask = opaque;
|
||||
iTask->complete = 1;
|
||||
if (iTask->co) {
|
||||
qemu_coroutine_enter(iTask->co, NULL);
|
||||
qemu_coroutine_enter(iTask->co);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
|
||||
|
||||
laiocb->ret = ret;
|
||||
if (laiocb->co) {
|
||||
qemu_coroutine_enter(laiocb->co, NULL);
|
||||
qemu_coroutine_enter(laiocb->co);
|
||||
} else {
|
||||
laiocb->common.cb(laiocb->common.opaque, ret);
|
||||
qemu_aio_unref(laiocb);
|
||||
|
@ -121,7 +121,7 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
|
||||
g_free(op);
|
||||
|
||||
if (s->waiting_for_io) {
|
||||
qemu_coroutine_enter(s->common.co, NULL);
|
||||
qemu_coroutine_enter(s->common.co);
|
||||
}
|
||||
}
|
||||
|
||||
@ -422,7 +422,9 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
assert(io_sectors);
|
||||
sector_num += io_sectors;
|
||||
nb_chunks -= DIV_ROUND_UP(io_sectors, sectors_per_chunk);
|
||||
delay_ns += ratelimit_calculate_delay(&s->limit, io_sectors);
|
||||
if (s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, io_sectors);
|
||||
}
|
||||
}
|
||||
return delay_ns;
|
||||
}
|
||||
@ -761,7 +763,8 @@ static void mirror_complete(BlockJob *job, Error **errp)
|
||||
target = blk_bs(s->target);
|
||||
|
||||
if (!s->synced) {
|
||||
error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id);
|
||||
error_setg(errp, "The active block job '%s' cannot be completed",
|
||||
job->id);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -842,8 +845,8 @@ static const BlockJobDriver commit_active_job_driver = {
|
||||
.attached_aio_context = mirror_attached_aio_context,
|
||||
};
|
||||
|
||||
static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
const char *replaces,
|
||||
static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *target, const char *replaces,
|
||||
int64_t speed, uint32_t granularity,
|
||||
int64_t buf_size,
|
||||
BlockMirrorBackingMode backing_mode,
|
||||
@ -872,7 +875,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
buf_size = DEFAULT_MIRROR_BUF_SIZE;
|
||||
}
|
||||
|
||||
s = block_job_create(driver, bs, speed, cb, opaque, errp);
|
||||
s = block_job_create(job_id, driver, bs, speed, cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
@ -900,13 +903,13 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
|
||||
|
||||
bdrv_op_block_all(target, s->common.blocker);
|
||||
|
||||
s->common.co = qemu_coroutine_create(mirror_run);
|
||||
s->common.co = qemu_coroutine_create(mirror_run, s);
|
||||
trace_mirror_start(bs, s, s->common.co, opaque);
|
||||
qemu_coroutine_enter(s->common.co, s);
|
||||
qemu_coroutine_enter(s->common.co);
|
||||
}
|
||||
|
||||
void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
const char *replaces,
|
||||
void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *target, const char *replaces,
|
||||
int64_t speed, uint32_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
|
||||
BlockdevOnError on_source_error,
|
||||
@ -924,14 +927,14 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
}
|
||||
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
|
||||
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
|
||||
mirror_start_job(bs, target, replaces,
|
||||
mirror_start_job(job_id, bs, target, replaces,
|
||||
speed, granularity, buf_size, backing_mode,
|
||||
on_source_error, on_target_error, unmap, cb, opaque, errp,
|
||||
&mirror_job_driver, is_none_mode, base);
|
||||
}
|
||||
|
||||
void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
int64_t speed,
|
||||
void commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *base, int64_t speed,
|
||||
BlockdevOnError on_error,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
@ -972,7 +975,8 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
}
|
||||
}
|
||||
|
||||
mirror_start_job(bs, base, NULL, speed, 0, 0, MIRROR_LEAVE_BACKING_CHAIN,
|
||||
mirror_start_job(job_id, bs, base, NULL, speed, 0, 0,
|
||||
MIRROR_LEAVE_BACKING_CHAIN,
|
||||
on_error, on_error, false, cb, opaque, &local_err,
|
||||
&commit_active_job_driver, false, base);
|
||||
if (local_err) {
|
||||
|
@ -38,7 +38,7 @@ static void nbd_recv_coroutines_enter_all(NbdClientSession *s)
|
||||
|
||||
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
|
||||
if (s->recv_coroutine[i]) {
|
||||
qemu_coroutine_enter(s->recv_coroutine[i], NULL);
|
||||
qemu_coroutine_enter(s->recv_coroutine[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,7 +99,7 @@ static void nbd_reply_ready(void *opaque)
|
||||
}
|
||||
|
||||
if (s->recv_coroutine[i]) {
|
||||
qemu_coroutine_enter(s->recv_coroutine[i], NULL);
|
||||
qemu_coroutine_enter(s->recv_coroutine[i]);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ static void nbd_restart_write(void *opaque)
|
||||
{
|
||||
BlockDriverState *bs = opaque;
|
||||
|
||||
qemu_coroutine_enter(nbd_get_client_session(bs)->send_coroutine, NULL);
|
||||
qemu_coroutine_enter(nbd_get_client_session(bs)->send_coroutine);
|
||||
}
|
||||
|
||||
static int nbd_co_send_request(BlockDriverState *bs,
|
||||
|
@ -104,7 +104,7 @@ static void nfs_co_generic_bh_cb(void *opaque)
|
||||
NFSRPC *task = opaque;
|
||||
task->complete = 1;
|
||||
qemu_bh_delete(task->bh);
|
||||
qemu_coroutine_enter(task->co, NULL);
|
||||
qemu_coroutine_enter(task->co);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -948,8 +948,8 @@ static int qcow_write(BlockDriverState *bs, int64_t sector_num,
|
||||
.nb_sectors = nb_sectors,
|
||||
.ret = -EINPROGRESS,
|
||||
};
|
||||
co = qemu_coroutine_create(qcow_write_co_entry);
|
||||
qemu_coroutine_enter(co, &data);
|
||||
co = qemu_coroutine_create(qcow_write_co_entry, &data);
|
||||
qemu_coroutine_enter(co);
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(aio_context, true);
|
||||
}
|
||||
|
@ -65,7 +65,8 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||
}
|
||||
}
|
||||
|
||||
if (new_l1_size > INT_MAX / sizeof(uint64_t)) {
|
||||
QEMU_BUILD_BUG_ON(QCOW_MAX_L1_SIZE > INT_MAX);
|
||||
if (new_l1_size > QCOW_MAX_L1_SIZE / sizeof(uint64_t)) {
|
||||
return -EFBIG;
|
||||
}
|
||||
|
||||
@ -482,8 +483,8 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
unsigned int l2_index;
|
||||
uint64_t l1_index, l2_offset, *l2_table;
|
||||
int l1_bits, c;
|
||||
unsigned int offset_in_cluster, nb_clusters;
|
||||
uint64_t bytes_available, bytes_needed;
|
||||
unsigned int offset_in_cluster;
|
||||
uint64_t bytes_available, bytes_needed, nb_clusters;
|
||||
int ret;
|
||||
|
||||
offset_in_cluster = offset_into_cluster(s, offset);
|
||||
@ -499,7 +500,6 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
if (bytes_needed > bytes_available) {
|
||||
bytes_needed = bytes_available;
|
||||
}
|
||||
assert(bytes_needed <= INT_MAX);
|
||||
|
||||
*cluster_offset = 0;
|
||||
|
||||
@ -536,8 +536,11 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1);
|
||||
*cluster_offset = be64_to_cpu(l2_table[l2_index]);
|
||||
|
||||
/* nb_needed <= INT_MAX, thus nb_clusters <= INT_MAX, too */
|
||||
nb_clusters = size_to_clusters(s, bytes_needed);
|
||||
/* bytes_needed <= *bytes + offset_in_cluster, both of which are unsigned
|
||||
* integers; the minimum cluster size is 512, so this assertion is always
|
||||
* true */
|
||||
assert(nb_clusters <= INT_MAX);
|
||||
|
||||
ret = qcow2_get_cluster_type(*cluster_offset);
|
||||
switch (ret) {
|
||||
@ -584,13 +587,17 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
|
||||
|
||||
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||
|
||||
bytes_available = (c * s->cluster_size);
|
||||
bytes_available = (int64_t)c * s->cluster_size;
|
||||
|
||||
out:
|
||||
if (bytes_available > bytes_needed) {
|
||||
bytes_available = bytes_needed;
|
||||
}
|
||||
|
||||
/* bytes_available <= bytes_needed <= *bytes + offset_in_cluster;
|
||||
* subtracting offset_in_cluster will therefore definitely yield something
|
||||
* not exceeding UINT_MAX */
|
||||
assert(bytes_available - offset_in_cluster <= UINT_MAX);
|
||||
*bytes = bytes_available - offset_in_cluster;
|
||||
|
||||
return ret;
|
||||
|
@ -2570,8 +2570,8 @@ static int qcow2_write(BlockDriverState *bs, int64_t sector_num,
|
||||
.nb_sectors = nb_sectors,
|
||||
.ret = -EINPROGRESS,
|
||||
};
|
||||
co = qemu_coroutine_create(qcow2_write_co_entry);
|
||||
qemu_coroutine_enter(co, &data);
|
||||
co = qemu_coroutine_create(qcow2_write_co_entry, &data);
|
||||
qemu_coroutine_enter(co);
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(aio_context, true);
|
||||
}
|
||||
|
@ -708,7 +708,7 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l
|
||||
}
|
||||
|
||||
if (cb->co) {
|
||||
qemu_coroutine_enter(cb->co, NULL);
|
||||
qemu_coroutine_enter(cb->co);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1425,7 +1425,7 @@ static void coroutine_fn qed_co_pwrite_zeroes_cb(void *opaque, int ret)
|
||||
cb->done = true;
|
||||
cb->ret = ret;
|
||||
if (cb->co) {
|
||||
qemu_coroutine_enter(cb->co, NULL);
|
||||
qemu_coroutine_enter(cb->co);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -639,15 +639,7 @@ static int raw_reopen_prepare(BDRVReopenState *state,
|
||||
|
||||
if ((raw_s->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) {
|
||||
/* dup the original fd */
|
||||
/* TODO: use qemu fcntl wrapper */
|
||||
#ifdef F_DUPFD_CLOEXEC
|
||||
raw_s->fd = fcntl(s->fd, F_DUPFD_CLOEXEC, 0);
|
||||
#else
|
||||
raw_s->fd = dup(s->fd);
|
||||
if (raw_s->fd != -1) {
|
||||
qemu_set_cloexec(raw_s->fd);
|
||||
}
|
||||
#endif
|
||||
raw_s->fd = qemu_dup(s->fd);
|
||||
if (raw_s->fd >= 0) {
|
||||
ret = fcntl_setfl(raw_s->fd, raw_s->open_flags);
|
||||
if (ret) {
|
||||
|
@ -495,7 +495,7 @@ static inline void free_aio_req(BDRVSheepdogState *s, AIOReq *aio_req)
|
||||
|
||||
static void coroutine_fn sd_finish_aiocb(SheepdogAIOCB *acb)
|
||||
{
|
||||
qemu_coroutine_enter(acb->coroutine, NULL);
|
||||
qemu_coroutine_enter(acb->coroutine);
|
||||
qemu_aio_unref(acb);
|
||||
}
|
||||
|
||||
@ -636,7 +636,7 @@ static void restart_co_req(void *opaque)
|
||||
{
|
||||
Coroutine *co = opaque;
|
||||
|
||||
qemu_coroutine_enter(co, NULL);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
|
||||
typedef struct SheepdogReqCo {
|
||||
@ -726,8 +726,8 @@ static int do_req(int sockfd, AioContext *aio_context, SheepdogReq *hdr,
|
||||
if (qemu_in_coroutine()) {
|
||||
do_co_req(&srco);
|
||||
} else {
|
||||
co = qemu_coroutine_create(do_co_req);
|
||||
qemu_coroutine_enter(co, &srco);
|
||||
co = qemu_coroutine_create(do_co_req, &srco);
|
||||
qemu_coroutine_enter(co);
|
||||
while (!srco.finished) {
|
||||
aio_poll(aio_context, true);
|
||||
}
|
||||
@ -925,17 +925,17 @@ static void co_read_response(void *opaque)
|
||||
BDRVSheepdogState *s = opaque;
|
||||
|
||||
if (!s->co_recv) {
|
||||
s->co_recv = qemu_coroutine_create(aio_read_response);
|
||||
s->co_recv = qemu_coroutine_create(aio_read_response, opaque);
|
||||
}
|
||||
|
||||
qemu_coroutine_enter(s->co_recv, opaque);
|
||||
qemu_coroutine_enter(s->co_recv);
|
||||
}
|
||||
|
||||
static void co_write_request(void *opaque)
|
||||
{
|
||||
BDRVSheepdogState *s = opaque;
|
||||
|
||||
qemu_coroutine_enter(s->co_send, NULL);
|
||||
qemu_coroutine_enter(s->co_send);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -777,7 +777,7 @@ static void restart_coroutine(void *opaque)
|
||||
|
||||
DPRINTF("co=%p", co);
|
||||
|
||||
qemu_coroutine_enter(co, NULL);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
|
||||
static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs)
|
||||
|
@ -95,6 +95,7 @@ static void coroutine_fn stream_run(void *opaque)
|
||||
BlockDriverState *base = s->base;
|
||||
int64_t sector_num = 0;
|
||||
int64_t end = -1;
|
||||
uint64_t delay_ns = 0;
|
||||
int error = 0;
|
||||
int ret = 0;
|
||||
int n = 0;
|
||||
@ -123,10 +124,8 @@ static void coroutine_fn stream_run(void *opaque)
|
||||
}
|
||||
|
||||
for (sector_num = 0; sector_num < end; sector_num += n) {
|
||||
uint64_t delay_ns = 0;
|
||||
bool copy;
|
||||
|
||||
wait:
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* with no pending I/O here so that bdrv_drain_all() returns.
|
||||
*/
|
||||
@ -156,12 +155,6 @@ wait:
|
||||
}
|
||||
trace_stream_one_iteration(s, sector_num, n, ret);
|
||||
if (copy) {
|
||||
if (s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
if (delay_ns > 0) {
|
||||
goto wait;
|
||||
}
|
||||
}
|
||||
ret = stream_populate(blk, sector_num, n, buf);
|
||||
}
|
||||
if (ret < 0) {
|
||||
@ -182,6 +175,9 @@ wait:
|
||||
|
||||
/* Publish progress */
|
||||
s->common.offset += n * BDRV_SECTOR_SIZE;
|
||||
if (copy && s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
}
|
||||
}
|
||||
|
||||
if (!base) {
|
||||
@ -218,15 +214,15 @@ static const BlockJobDriver stream_job_driver = {
|
||||
.set_speed = stream_set_speed,
|
||||
};
|
||||
|
||||
void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
const char *backing_file_str, int64_t speed,
|
||||
BlockdevOnError on_error,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
void stream_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *base, const char *backing_file_str,
|
||||
int64_t speed, BlockdevOnError on_error,
|
||||
BlockCompletionFunc *cb, void *opaque, Error **errp)
|
||||
{
|
||||
StreamBlockJob *s;
|
||||
|
||||
s = block_job_create(&stream_job_driver, bs, speed, cb, opaque, errp);
|
||||
s = block_job_create(job_id, &stream_job_driver, bs, speed,
|
||||
cb, opaque, errp);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
@ -235,7 +231,7 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
s->backing_file_str = g_strdup(backing_file_str);
|
||||
|
||||
s->on_error = on_error;
|
||||
s->common.co = qemu_coroutine_create(stream_run);
|
||||
s->common.co = qemu_coroutine_create(stream_run, s);
|
||||
trace_stream_start(bs, base, s, s->common.co, opaque);
|
||||
qemu_coroutine_enter(s->common.co, s);
|
||||
qemu_coroutine_enter(s->common.co);
|
||||
}
|
||||
|
18
block/vmdk.c
18
block/vmdk.c
@ -1202,13 +1202,6 @@ static int get_cluster_offset(BlockDriverState *bs,
|
||||
l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size;
|
||||
cluster_sector = le32_to_cpu(l2_table[l2_index]);
|
||||
|
||||
if (m_data) {
|
||||
m_data->valid = 1;
|
||||
m_data->l1_index = l1_index;
|
||||
m_data->l2_index = l2_index;
|
||||
m_data->l2_offset = l2_offset;
|
||||
m_data->l2_cache_entry = &l2_table[l2_index];
|
||||
}
|
||||
if (extent->has_zero_grain && cluster_sector == VMDK_GTE_ZEROED) {
|
||||
zeroed = true;
|
||||
}
|
||||
@ -1231,6 +1224,13 @@ static int get_cluster_offset(BlockDriverState *bs,
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
if (m_data) {
|
||||
m_data->valid = 1;
|
||||
m_data->l1_index = l1_index;
|
||||
m_data->l2_index = l2_index;
|
||||
m_data->l2_offset = l2_offset;
|
||||
m_data->l2_cache_entry = &l2_table[l2_index];
|
||||
}
|
||||
}
|
||||
*cluster_offset = cluster_sector << BDRV_SECTOR_BITS;
|
||||
return VMDK_OK;
|
||||
@ -1686,8 +1686,8 @@ static int vmdk_write_compressed(BlockDriverState *bs,
|
||||
.nb_sectors = nb_sectors,
|
||||
.ret = -EINPROGRESS,
|
||||
};
|
||||
co = qemu_coroutine_create(vmdk_co_write_compressed);
|
||||
qemu_coroutine_enter(co, &data);
|
||||
co = qemu_coroutine_create(vmdk_co_write_compressed, &data);
|
||||
qemu_coroutine_enter(co);
|
||||
while (data.ret == -EINPROGRESS) {
|
||||
aio_poll(aio_context, true);
|
||||
}
|
||||
|
@ -3018,9 +3018,10 @@ static int enable_write_target(BlockDriverState *bs, Error **errp)
|
||||
}
|
||||
|
||||
options = qdict_new();
|
||||
qdict_put(options, "driver", qstring_from_str("qcow"));
|
||||
qdict_put(options, "write-target.driver", qstring_from_str("qcow"));
|
||||
s->qcow = bdrv_open_child(s->qcow_filename, options, "write-target", bs,
|
||||
&child_vvfat_qcow, false, errp);
|
||||
QDECREF(options);
|
||||
if (!s->qcow) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
|
119
blockdev.c
119
blockdev.c
@ -512,6 +512,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
|
||||
writethrough = !qemu_opt_get_bool(opts, BDRV_OPT_CACHE_WB, true);
|
||||
|
||||
id = qemu_opts_id(opts);
|
||||
|
||||
qdict_extract_subqdict(bs_opts, &interval_dict, "stats-intervals.");
|
||||
qdict_array_split(interval_dict, &interval_list);
|
||||
|
||||
@ -616,7 +618,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
/* disk I/O throttling */
|
||||
if (throttle_enabled(&cfg)) {
|
||||
if (!throttling_group) {
|
||||
throttling_group = blk_name(blk);
|
||||
throttling_group = id;
|
||||
}
|
||||
blk_io_limits_enable(blk, throttling_group);
|
||||
blk_set_io_limits(blk, &cfg);
|
||||
@ -625,7 +627,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
|
||||
blk_set_enable_write_cache(blk, !writethrough);
|
||||
blk_set_on_error(blk, on_read_error, on_write_error);
|
||||
|
||||
if (!monitor_add_blk(blk, qemu_opts_id(opts), errp)) {
|
||||
if (!monitor_add_blk(blk, id, errp)) {
|
||||
blk_unref(blk);
|
||||
blk = NULL;
|
||||
goto err_no_bs_opts;
|
||||
@ -1836,9 +1838,9 @@ typedef struct DriveBackupState {
|
||||
BlockJob *job;
|
||||
} DriveBackupState;
|
||||
|
||||
static void do_drive_backup(const char *device, const char *target,
|
||||
bool has_format, const char *format,
|
||||
enum MirrorSyncMode sync,
|
||||
static void do_drive_backup(const char *job_id, const char *device,
|
||||
const char *target, bool has_format,
|
||||
const char *format, enum MirrorSyncMode sync,
|
||||
bool has_mode, enum NewImageMode mode,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_bitmap, const char *bitmap,
|
||||
@ -1876,7 +1878,8 @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
|
||||
bdrv_drained_begin(blk_bs(blk));
|
||||
state->bs = blk_bs(blk);
|
||||
|
||||
do_drive_backup(backup->device, backup->target,
|
||||
do_drive_backup(backup->has_job_id ? backup->job_id : NULL,
|
||||
backup->device, backup->target,
|
||||
backup->has_format, backup->format,
|
||||
backup->sync,
|
||||
backup->has_mode, backup->mode,
|
||||
@ -1921,8 +1924,8 @@ typedef struct BlockdevBackupState {
|
||||
AioContext *aio_context;
|
||||
} BlockdevBackupState;
|
||||
|
||||
static void do_blockdev_backup(const char *device, const char *target,
|
||||
enum MirrorSyncMode sync,
|
||||
static void do_blockdev_backup(const char *job_id, const char *device,
|
||||
const char *target, enum MirrorSyncMode sync,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_on_source_error,
|
||||
BlockdevOnError on_source_error,
|
||||
@ -1968,8 +1971,8 @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
|
||||
state->bs = blk_bs(blk);
|
||||
bdrv_drained_begin(state->bs);
|
||||
|
||||
do_blockdev_backup(backup->device, backup->target,
|
||||
backup->sync,
|
||||
do_blockdev_backup(backup->has_job_id ? backup->job_id : NULL,
|
||||
backup->device, backup->target, backup->sync,
|
||||
backup->has_speed, backup->speed,
|
||||
backup->has_on_source_error, backup->on_source_error,
|
||||
backup->has_on_target_error, backup->on_target_error,
|
||||
@ -3004,7 +3007,7 @@ static void block_job_cb(void *opaque, int ret)
|
||||
}
|
||||
}
|
||||
|
||||
void qmp_block_stream(const char *device,
|
||||
void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
|
||||
bool has_base, const char *base,
|
||||
bool has_backing_file, const char *backing_file,
|
||||
bool has_speed, int64_t speed,
|
||||
@ -3063,8 +3066,8 @@ void qmp_block_stream(const char *device,
|
||||
/* backing_file string overrides base bs filename */
|
||||
base_name = has_backing_file ? backing_file : base_name;
|
||||
|
||||
stream_start(bs, base_bs, base_name, has_speed ? speed : 0,
|
||||
on_error, block_job_cb, bs, &local_err);
|
||||
stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name,
|
||||
has_speed ? speed : 0, on_error, block_job_cb, bs, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
goto out;
|
||||
@ -3076,7 +3079,7 @@ out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_block_commit(const char *device,
|
||||
void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
|
||||
bool has_base, const char *base,
|
||||
bool has_top, const char *top,
|
||||
bool has_backing_file, const char *backing_file,
|
||||
@ -3167,10 +3170,11 @@ void qmp_block_commit(const char *device,
|
||||
" but 'top' is the active layer");
|
||||
goto out;
|
||||
}
|
||||
commit_active_start(bs, base_bs, speed, on_error, block_job_cb,
|
||||
bs, &local_err);
|
||||
commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, speed,
|
||||
on_error, block_job_cb, bs, &local_err);
|
||||
} else {
|
||||
commit_start(bs, base_bs, top_bs, speed, on_error, block_job_cb, bs,
|
||||
commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, speed,
|
||||
on_error, block_job_cb, bs,
|
||||
has_backing_file ? backing_file : NULL, &local_err);
|
||||
}
|
||||
if (local_err != NULL) {
|
||||
@ -3182,9 +3186,9 @@ out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
static void do_drive_backup(const char *device, const char *target,
|
||||
bool has_format, const char *format,
|
||||
enum MirrorSyncMode sync,
|
||||
static void do_drive_backup(const char *job_id, const char *device,
|
||||
const char *target, bool has_format,
|
||||
const char *format, enum MirrorSyncMode sync,
|
||||
bool has_mode, enum NewImageMode mode,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_bitmap, const char *bitmap,
|
||||
@ -3303,7 +3307,7 @@ static void do_drive_backup(const char *device, const char *target,
|
||||
}
|
||||
}
|
||||
|
||||
backup_start(bs, target_bs, speed, sync, bmap,
|
||||
backup_start(job_id, bs, target_bs, speed, sync, bmap,
|
||||
on_source_error, on_target_error,
|
||||
block_job_cb, bs, txn, &local_err);
|
||||
bdrv_unref(target_bs);
|
||||
@ -3316,7 +3320,8 @@ out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_drive_backup(const char *device, const char *target,
|
||||
void qmp_drive_backup(bool has_job_id, const char *job_id,
|
||||
const char *device, const char *target,
|
||||
bool has_format, const char *format,
|
||||
enum MirrorSyncMode sync,
|
||||
bool has_mode, enum NewImageMode mode,
|
||||
@ -3326,7 +3331,8 @@ void qmp_drive_backup(const char *device, const char *target,
|
||||
bool has_on_target_error, BlockdevOnError on_target_error,
|
||||
Error **errp)
|
||||
{
|
||||
return do_drive_backup(device, target, has_format, format, sync,
|
||||
return do_drive_backup(has_job_id ? job_id : NULL, device, target,
|
||||
has_format, format, sync,
|
||||
has_mode, mode, has_speed, speed,
|
||||
has_bitmap, bitmap,
|
||||
has_on_source_error, on_source_error,
|
||||
@ -3339,8 +3345,8 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
|
||||
return bdrv_named_nodes_list(errp);
|
||||
}
|
||||
|
||||
void do_blockdev_backup(const char *device, const char *target,
|
||||
enum MirrorSyncMode sync,
|
||||
void do_blockdev_backup(const char *job_id, const char *device,
|
||||
const char *target, enum MirrorSyncMode sync,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_on_source_error,
|
||||
BlockdevOnError on_source_error,
|
||||
@ -3395,7 +3401,7 @@ void do_blockdev_backup(const char *device, const char *target,
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
backup_start(bs, target_bs, speed, sync, NULL, on_source_error,
|
||||
backup_start(job_id, bs, target_bs, speed, sync, NULL, on_source_error,
|
||||
on_target_error, block_job_cb, bs, txn, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -3404,7 +3410,8 @@ out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_blockdev_backup(const char *device, const char *target,
|
||||
void qmp_blockdev_backup(bool has_job_id, const char *job_id,
|
||||
const char *device, const char *target,
|
||||
enum MirrorSyncMode sync,
|
||||
bool has_speed, int64_t speed,
|
||||
bool has_on_source_error,
|
||||
@ -3413,7 +3420,8 @@ void qmp_blockdev_backup(const char *device, const char *target,
|
||||
BlockdevOnError on_target_error,
|
||||
Error **errp)
|
||||
{
|
||||
do_blockdev_backup(device, target, sync, has_speed, speed,
|
||||
do_blockdev_backup(has_job_id ? job_id : NULL, device, target,
|
||||
sync, has_speed, speed,
|
||||
has_on_source_error, on_source_error,
|
||||
has_on_target_error, on_target_error,
|
||||
NULL, errp);
|
||||
@ -3422,7 +3430,7 @@ void qmp_blockdev_backup(const char *device, const char *target,
|
||||
/* Parameter check and block job starting for drive mirroring.
|
||||
* Caller should hold @device and @target's aio context (must be the same).
|
||||
**/
|
||||
static void blockdev_mirror_common(BlockDriverState *bs,
|
||||
static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *target,
|
||||
bool has_replaces, const char *replaces,
|
||||
enum MirrorSyncMode sync,
|
||||
@ -3482,15 +3490,15 @@ static void blockdev_mirror_common(BlockDriverState *bs,
|
||||
/* pass the node name to replace to mirror start since it's loose coupling
|
||||
* and will allow to check whether the node still exist at mirror completion
|
||||
*/
|
||||
mirror_start(bs, target,
|
||||
mirror_start(job_id, bs, target,
|
||||
has_replaces ? replaces : NULL,
|
||||
speed, granularity, buf_size, sync, backing_mode,
|
||||
on_source_error, on_target_error, unmap,
|
||||
block_job_cb, bs, errp);
|
||||
}
|
||||
|
||||
void qmp_drive_mirror(const char *device, const char *target,
|
||||
bool has_format, const char *format,
|
||||
void qmp_drive_mirror(bool has_job_id, const char *job_id, const char *device,
|
||||
const char *target, bool has_format, const char *format,
|
||||
bool has_node_name, const char *node_name,
|
||||
bool has_replaces, const char *replaces,
|
||||
enum MirrorSyncMode sync,
|
||||
@ -3634,7 +3642,7 @@ void qmp_drive_mirror(const char *device, const char *target,
|
||||
|
||||
bdrv_set_aio_context(target_bs, aio_context);
|
||||
|
||||
blockdev_mirror_common(bs, target_bs,
|
||||
blockdev_mirror_common(has_job_id ? job_id : NULL, bs, target_bs,
|
||||
has_replaces, replaces, sync, backing_mode,
|
||||
has_speed, speed,
|
||||
has_granularity, granularity,
|
||||
@ -3649,7 +3657,8 @@ out:
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
void qmp_blockdev_mirror(const char *device, const char *target,
|
||||
void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
|
||||
const char *device, const char *target,
|
||||
bool has_replaces, const char *replaces,
|
||||
MirrorSyncMode sync,
|
||||
bool has_speed, int64_t speed,
|
||||
@ -3690,7 +3699,7 @@ void qmp_blockdev_mirror(const char *device, const char *target,
|
||||
|
||||
bdrv_set_aio_context(target_bs, aio_context);
|
||||
|
||||
blockdev_mirror_common(bs, target_bs,
|
||||
blockdev_mirror_common(has_job_id ? job_id : NULL, bs, target_bs,
|
||||
has_replaces, replaces, sync, backing_mode,
|
||||
has_speed, speed,
|
||||
has_granularity, granularity,
|
||||
@ -3704,42 +3713,28 @@ void qmp_blockdev_mirror(const char *device, const char *target,
|
||||
aio_context_release(aio_context);
|
||||
}
|
||||
|
||||
/* Get the block job for a given device name and acquire its AioContext */
|
||||
static BlockJob *find_block_job(const char *device, AioContext **aio_context,
|
||||
/* Get a block job using its ID and acquire its AioContext */
|
||||
static BlockJob *find_block_job(const char *id, AioContext **aio_context,
|
||||
Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
BlockDriverState *bs;
|
||||
BlockJob *job;
|
||||
|
||||
assert(id != NULL);
|
||||
|
||||
*aio_context = NULL;
|
||||
|
||||
blk = blk_by_name(device);
|
||||
if (!blk) {
|
||||
goto notfound;
|
||||
job = block_job_get(id);
|
||||
|
||||
if (!job) {
|
||||
error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE,
|
||||
"Block job '%s' not found", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*aio_context = blk_get_aio_context(blk);
|
||||
*aio_context = blk_get_aio_context(job->blk);
|
||||
aio_context_acquire(*aio_context);
|
||||
|
||||
if (!blk_is_available(blk)) {
|
||||
goto notfound;
|
||||
}
|
||||
bs = blk_bs(blk);
|
||||
|
||||
if (!bs->job) {
|
||||
goto notfound;
|
||||
}
|
||||
|
||||
return bs->job;
|
||||
|
||||
notfound:
|
||||
error_set(errp, ERROR_CLASS_DEVICE_NOT_ACTIVE,
|
||||
"No active block job on device '%s'", device);
|
||||
if (*aio_context) {
|
||||
aio_context_release(*aio_context);
|
||||
*aio_context = NULL;
|
||||
}
|
||||
return NULL;
|
||||
return job;
|
||||
}
|
||||
|
||||
void qmp_block_job_set_speed(const char *device, int64_t speed, Error **errp)
|
||||
|
42
blockjob.c
42
blockjob.c
@ -33,6 +33,7 @@
|
||||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qmp/qjson.h"
|
||||
#include "qemu/coroutine.h"
|
||||
#include "qemu/id.h"
|
||||
#include "qmp-commands.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi-event.h"
|
||||
@ -60,6 +61,19 @@ BlockJob *block_job_next(BlockJob *job)
|
||||
return QLIST_NEXT(job, job_list);
|
||||
}
|
||||
|
||||
BlockJob *block_job_get(const char *id)
|
||||
{
|
||||
BlockJob *job;
|
||||
|
||||
QLIST_FOREACH(job, &block_jobs, job_list) {
|
||||
if (!strcmp(id, job->id)) {
|
||||
return job;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Normally the job runs in its BlockBackend's AioContext. The exception is
|
||||
* block_job_defer_to_main_loop() where it runs in the QEMU main loop. Code
|
||||
* that supports both cases uses this helper function.
|
||||
@ -103,9 +117,9 @@ static void block_job_detach_aio_context(void *opaque)
|
||||
block_job_unref(job);
|
||||
}
|
||||
|
||||
void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
int64_t speed, BlockCompletionFunc *cb,
|
||||
void *opaque, Error **errp)
|
||||
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
||||
BlockDriverState *bs, int64_t speed,
|
||||
BlockCompletionFunc *cb, void *opaque, Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
BlockJob *job;
|
||||
@ -116,6 +130,20 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (job_id == NULL) {
|
||||
job_id = bdrv_get_device_name(bs);
|
||||
}
|
||||
|
||||
if (!id_wellformed(job_id)) {
|
||||
error_setg(errp, "Invalid job ID '%s'", job_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (block_job_get(job_id)) {
|
||||
error_setg(errp, "Job ID '%s' already in use", job_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
blk = blk_new();
|
||||
blk_insert_bs(blk, bs);
|
||||
|
||||
@ -126,7 +154,7 @@ void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
|
||||
|
||||
job->driver = driver;
|
||||
job->id = g_strdup(bdrv_get_device_name(bs));
|
||||
job->id = g_strdup(job_id);
|
||||
job->blk = blk;
|
||||
job->cb = cb;
|
||||
job->opaque = opaque;
|
||||
@ -290,7 +318,8 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
void block_job_complete(BlockJob *job, Error **errp)
|
||||
{
|
||||
if (job->pause_count || job->cancelled || !job->driver->complete) {
|
||||
error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id);
|
||||
error_setg(errp, "The active block job '%s' cannot be completed",
|
||||
job->id);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -346,7 +375,7 @@ void block_job_resume(BlockJob *job)
|
||||
void block_job_enter(BlockJob *job)
|
||||
{
|
||||
if (job->co && !job->busy) {
|
||||
qemu_coroutine_enter(job->co, NULL);
|
||||
qemu_coroutine_enter(job->co);
|
||||
}
|
||||
}
|
||||
|
||||
@ -524,6 +553,7 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
|
||||
|
||||
switch (on_err) {
|
||||
case BLOCKDEV_ON_ERROR_ENOSPC:
|
||||
case BLOCKDEV_ON_ERROR_AUTO:
|
||||
action = (error == ENOSPC) ?
|
||||
BLOCK_ERROR_ACTION_STOP : BLOCK_ERROR_ACTION_REPORT;
|
||||
break;
|
||||
|
@ -92,7 +92,8 @@ Data:
|
||||
|
||||
- "type": Job type (json-string; "stream" for image streaming
|
||||
"commit" for block commit)
|
||||
- "device": Device name (json-string)
|
||||
- "device": Job identifier. Originally the device name but other
|
||||
values are allowed since QEMU 2.7 (json-string)
|
||||
- "len": Maximum progress value (json-int)
|
||||
- "offset": Current progress value (json-int)
|
||||
On success this is equal to len.
|
||||
@ -116,7 +117,8 @@ Data:
|
||||
|
||||
- "type": Job type (json-string; "stream" for image streaming
|
||||
"commit" for block commit)
|
||||
- "device": Device name (json-string)
|
||||
- "device": Job identifier. Originally the device name but other
|
||||
values are allowed since QEMU 2.7 (json-string)
|
||||
- "len": Maximum progress value (json-int)
|
||||
- "offset": Current progress value (json-int)
|
||||
On success this is equal to len.
|
||||
@ -143,7 +145,8 @@ Emitted when a block job encounters an error.
|
||||
|
||||
Data:
|
||||
|
||||
- "device": device name (json-string)
|
||||
- "device": Job identifier. Originally the device name but other
|
||||
values are allowed since QEMU 2.7 (json-string)
|
||||
- "operation": I/O operation (json-string, "read" or "write")
|
||||
- "action": action that has been taken, it's one of the following (json-string):
|
||||
"ignore": error has been ignored, the job may fail later
|
||||
@ -167,7 +170,8 @@ Data:
|
||||
|
||||
- "type": Job type (json-string; "stream" for image streaming
|
||||
"commit" for block commit)
|
||||
- "device": Device name (json-string)
|
||||
- "device": Job identifier. Originally the device name but other
|
||||
values are allowed since QEMU 2.7 (json-string)
|
||||
- "len": Maximum progress value (json-int)
|
||||
- "offset": Current progress value (json-int)
|
||||
On success this is equal to len.
|
||||
|
6
hmp.c
6
hmp.c
@ -1097,7 +1097,7 @@ void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
|
||||
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||
}
|
||||
|
||||
qmp_drive_mirror(device, filename, !!format, format,
|
||||
qmp_drive_mirror(false, NULL, device, filename, !!format, format,
|
||||
false, NULL, false, NULL,
|
||||
full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
|
||||
true, mode, false, 0, false, 0, false, 0,
|
||||
@ -1127,7 +1127,7 @@ void hmp_drive_backup(Monitor *mon, const QDict *qdict)
|
||||
mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
|
||||
}
|
||||
|
||||
qmp_drive_backup(device, filename, !!format, format,
|
||||
qmp_drive_backup(false, NULL, device, filename, !!format, format,
|
||||
full ? MIRROR_SYNC_MODE_FULL : MIRROR_SYNC_MODE_TOP,
|
||||
true, mode, false, 0, false, NULL,
|
||||
false, 0, false, 0, &err);
|
||||
@ -1485,7 +1485,7 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict)
|
||||
const char *base = qdict_get_try_str(qdict, "base");
|
||||
int64_t speed = qdict_get_try_int(qdict, "speed", 0);
|
||||
|
||||
qmp_block_stream(device, base != NULL, base, false, NULL,
|
||||
qmp_block_stream(false, NULL, device, base != NULL, base, false, NULL,
|
||||
qdict_haskey(qdict, "speed"), speed,
|
||||
true, BLOCKDEV_ON_ERROR_REPORT, &error);
|
||||
|
||||
|
@ -3278,8 +3278,8 @@ void pdu_submit(V9fsPDU *pdu)
|
||||
if (is_ro_export(&s->ctx) && !is_read_only_op(pdu)) {
|
||||
handler = v9fs_fs_ro;
|
||||
}
|
||||
co = qemu_coroutine_create(handler);
|
||||
qemu_coroutine_enter(co, pdu);
|
||||
co = qemu_coroutine_create(handler, pdu);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
|
||||
/* Returns 0 on success, 1 on failure. */
|
||||
|
@ -22,14 +22,14 @@
|
||||
static void coroutine_enter_cb(void *opaque, int ret)
|
||||
{
|
||||
Coroutine *co = opaque;
|
||||
qemu_coroutine_enter(co, NULL);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
|
||||
/* Called from worker thread. */
|
||||
static int coroutine_enter_func(void *arg)
|
||||
{
|
||||
Coroutine *co = arg;
|
||||
qemu_coroutine_enter(co, NULL);
|
||||
qemu_coroutine_enter(co);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,34 @@ void blkconf_blocksizes(BlockConf *conf)
|
||||
}
|
||||
}
|
||||
|
||||
void blkconf_apply_backend_options(BlockConf *conf)
|
||||
{
|
||||
BlockBackend *blk = conf->blk;
|
||||
BlockdevOnError rerror, werror;
|
||||
bool wce;
|
||||
|
||||
switch (conf->wce) {
|
||||
case ON_OFF_AUTO_ON: wce = true; break;
|
||||
case ON_OFF_AUTO_OFF: wce = false; break;
|
||||
case ON_OFF_AUTO_AUTO: wce = blk_enable_write_cache(blk); break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
rerror = conf->rerror;
|
||||
if (rerror == BLOCKDEV_ON_ERROR_AUTO) {
|
||||
rerror = blk_get_on_error(blk, true);
|
||||
}
|
||||
|
||||
werror = conf->werror;
|
||||
if (werror == BLOCKDEV_ON_ERROR_AUTO) {
|
||||
werror = blk_get_on_error(blk, false);
|
||||
}
|
||||
|
||||
blk_set_enable_write_cache(blk, wce);
|
||||
blk_set_on_error(blk, rerror, werror);
|
||||
}
|
||||
|
||||
void blkconf_geometry(BlockConf *conf, int *ptrans,
|
||||
unsigned cyls_max, unsigned heads_max, unsigned secs_max,
|
||||
Error **errp)
|
||||
|
@ -803,6 +803,7 @@ static int nvme_init(PCIDevice *pci_dev)
|
||||
return -1;
|
||||
}
|
||||
blkconf_blocksizes(&n->conf);
|
||||
blkconf_apply_backend_options(&n->conf);
|
||||
|
||||
pci_conf = pci_dev->config;
|
||||
pci_conf[PCI_INTERRUPT_PIN] = 1;
|
||||
|
@ -897,6 +897,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
blkconf_serial(&conf->conf, &conf->serial);
|
||||
blkconf_apply_backend_options(&conf->conf);
|
||||
s->original_wce = blk_enable_write_cache(conf->conf.blk);
|
||||
blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, &err);
|
||||
if (err) {
|
||||
@ -959,6 +960,7 @@ static void virtio_blk_instance_init(Object *obj)
|
||||
|
||||
static Property virtio_blk_properties[] = {
|
||||
DEFINE_BLOCK_PROPERTIES(VirtIOBlock, conf.conf),
|
||||
DEFINE_BLOCK_ERROR_PROPERTIES(VirtIOBlock, conf.conf),
|
||||
DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlock, conf.conf),
|
||||
DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial),
|
||||
DEFINE_PROP_BIT("config-wce", VirtIOBlock, conf.config_wce, 0, true),
|
||||
|
@ -72,12 +72,21 @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr,
|
||||
const char *propname, Error **errp)
|
||||
{
|
||||
BlockBackend *blk;
|
||||
bool blk_created = false;
|
||||
|
||||
blk = blk_by_name(str);
|
||||
if (!blk) {
|
||||
BlockDriverState *bs = bdrv_lookup_bs(NULL, str, NULL);
|
||||
if (bs) {
|
||||
blk = blk_new();
|
||||
blk_insert_bs(blk, bs);
|
||||
blk_created = true;
|
||||
}
|
||||
}
|
||||
if (!blk) {
|
||||
error_setg(errp, "Property '%s.%s' can't find value '%s'",
|
||||
object_get_typename(OBJECT(dev)), propname, str);
|
||||
return;
|
||||
goto fail;
|
||||
}
|
||||
if (blk_attach_dev(blk, dev) < 0) {
|
||||
DriveInfo *dinfo = blk_legacy_dinfo(blk);
|
||||
@ -91,9 +100,16 @@ static void parse_drive(DeviceState *dev, const char *str, void **ptr,
|
||||
error_setg(errp, "Drive '%s' is already in use by another device",
|
||||
str);
|
||||
}
|
||||
return;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*ptr = blk;
|
||||
|
||||
fail:
|
||||
if (blk_created) {
|
||||
/* If we need to keep a reference, blk_attach_dev() took it */
|
||||
blk_unref(blk);
|
||||
}
|
||||
}
|
||||
|
||||
static void release_drive(Object *obj, const char *name, void *opaque)
|
||||
@ -103,8 +119,8 @@ static void release_drive(Object *obj, const char *name, void *opaque)
|
||||
BlockBackend **ptr = qdev_get_prop_ptr(dev, prop);
|
||||
|
||||
if (*ptr) {
|
||||
blk_detach_dev(*ptr, dev);
|
||||
blockdev_auto_del(*ptr);
|
||||
blk_detach_dev(*ptr, dev);
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,7 +143,7 @@ static void set_drive(Object *obj, Visitor *v, const char *name, void *opaque,
|
||||
|
||||
PropertyInfo qdev_prop_drive = {
|
||||
.name = "str",
|
||||
.description = "ID of a drive to use as a backend",
|
||||
.description = "Node name or ID of a block device to use as a backend",
|
||||
.get = get_drive,
|
||||
.set = set_drive,
|
||||
.release = release_drive,
|
||||
@ -362,8 +378,19 @@ PropertyInfo qdev_prop_vlan = {
|
||||
void qdev_prop_set_drive(DeviceState *dev, const char *name,
|
||||
BlockBackend *value, Error **errp)
|
||||
{
|
||||
object_property_set_str(OBJECT(dev), value ? blk_name(value) : "",
|
||||
name, errp);
|
||||
const char *ref = "";
|
||||
|
||||
if (value) {
|
||||
ref = blk_name(value);
|
||||
if (!*ref) {
|
||||
BlockDriverState *bs = blk_bs(value);
|
||||
if (bs) {
|
||||
ref = bdrv_get_node_name(bs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object_property_set_str(OBJECT(dev), ref, name, errp);
|
||||
}
|
||||
|
||||
void qdev_prop_set_chr(DeviceState *dev, const char *name,
|
||||
|
@ -539,6 +539,19 @@ PropertyInfo qdev_prop_losttickpolicy = {
|
||||
.set = set_enum,
|
||||
};
|
||||
|
||||
/* --- Block device error handling policy --- */
|
||||
|
||||
QEMU_BUILD_BUG_ON(sizeof(BlockdevOnError) != sizeof(int));
|
||||
|
||||
PropertyInfo qdev_prop_blockdev_on_error = {
|
||||
.name = "BlockdevOnError",
|
||||
.description = "Error handling policy, "
|
||||
"report/ignore/enospc/stop/auto",
|
||||
.enum_table = BlockdevOnError_lookup,
|
||||
.get = get_enum,
|
||||
.set = set_enum,
|
||||
};
|
||||
|
||||
/* --- BIOS CHS translation */
|
||||
|
||||
QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int));
|
||||
|
@ -180,6 +180,7 @@ static int ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
blkconf_apply_backend_options(&dev->conf);
|
||||
|
||||
if (ide_init_drive(s, dev->conf.blk, kind,
|
||||
dev->version, dev->serial, dev->model, dev->wwn,
|
||||
@ -263,6 +264,7 @@ static int ide_drive_initfn(IDEDevice *dev)
|
||||
|
||||
#define DEFINE_IDE_DEV_PROPERTIES() \
|
||||
DEFINE_BLOCK_PROPERTIES(IDEDrive, dev.conf), \
|
||||
DEFINE_BLOCK_ERROR_PROPERTIES(IDEDrive, dev.conf), \
|
||||
DEFINE_PROP_STRING("ver", IDEDrive, dev.version), \
|
||||
DEFINE_PROP_UINT64("wwn", IDEDrive, dev.wwn, 0), \
|
||||
DEFINE_PROP_STRING("serial", IDEDrive, dev.serial),\
|
||||
|
@ -2309,6 +2309,7 @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
|
||||
return;
|
||||
}
|
||||
}
|
||||
blkconf_apply_backend_options(&dev->conf);
|
||||
|
||||
if (s->qdev.conf.discard_granularity == -1) {
|
||||
s->qdev.conf.discard_granularity =
|
||||
@ -2848,6 +2849,7 @@ static const TypeInfo scsi_disk_base_info = {
|
||||
|
||||
#define DEFINE_SCSI_DISK_PROPERTIES() \
|
||||
DEFINE_BLOCK_PROPERTIES(SCSIDiskState, qdev.conf), \
|
||||
DEFINE_BLOCK_ERROR_PROPERTIES(SCSIDiskState, qdev.conf), \
|
||||
DEFINE_PROP_STRING("ver", SCSIDiskState, version), \
|
||||
DEFINE_PROP_STRING("serial", SCSIDiskState, serial), \
|
||||
DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor), \
|
||||
|
@ -603,16 +603,19 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
|
||||
|
||||
blkconf_serial(&s->conf, &dev->serial);
|
||||
blkconf_blocksizes(&s->conf);
|
||||
blkconf_apply_backend_options(&s->conf);
|
||||
|
||||
/*
|
||||
* Hack alert: this pretends to be a block device, but it's really
|
||||
* a SCSI bus that can serve only a single device, which it
|
||||
* creates automatically. But first it needs to detach from its
|
||||
* blockdev, or else scsi_bus_legacy_add_drive() dies when it
|
||||
* attaches again.
|
||||
* attaches again. We also need to take another reference so that
|
||||
* blk_detach_dev() doesn't free blk while we still need it.
|
||||
*
|
||||
* The hack is probably a bad idea.
|
||||
*/
|
||||
blk_ref(blk);
|
||||
blk_detach_dev(blk, &s->dev.qdev);
|
||||
s->conf.blk = NULL;
|
||||
|
||||
@ -623,6 +626,7 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
|
||||
scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable,
|
||||
s->conf.bootindex, dev->serial,
|
||||
&err);
|
||||
blk_unref(blk);
|
||||
if (!scsi_dev) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
|
@ -639,11 +639,13 @@ int is_windows_drive(const char *filename);
|
||||
|
||||
/**
|
||||
* stream_start:
|
||||
* @job_id: The id of the newly-created job, or %NULL to use the
|
||||
* device name of @bs.
|
||||
* @bs: Block device to operate on.
|
||||
* @base: Block device that will become the new base, or %NULL to
|
||||
* flatten the whole backing file chain onto @bs.
|
||||
* @base_id: The file name that will be written to @bs as the new
|
||||
* backing file if the job completes. Ignored if @base is %NULL.
|
||||
* @backing_file_str: The file name that will be written to @bs as the
|
||||
* the new backing file if the job completes. Ignored if @base is %NULL.
|
||||
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
|
||||
* @on_error: The action to take upon error.
|
||||
* @cb: Completion function for the job.
|
||||
@ -654,15 +656,18 @@ int is_windows_drive(const char *filename);
|
||||
* in @bs, but allocated in any image between @base and @bs (both
|
||||
* exclusive) will be written to @bs. At the end of a successful
|
||||
* streaming job, the backing file of @bs will be changed to
|
||||
* @base_id in the written image and to @base in the live BlockDriverState.
|
||||
* @backing_file_str in the written image and to @base in the live
|
||||
* BlockDriverState.
|
||||
*/
|
||||
void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
const char *base_id, int64_t speed, BlockdevOnError on_error,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque, Error **errp);
|
||||
void stream_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *base, const char *backing_file_str,
|
||||
int64_t speed, BlockdevOnError on_error,
|
||||
BlockCompletionFunc *cb, void *opaque, Error **errp);
|
||||
|
||||
/**
|
||||
* commit_start:
|
||||
* @job_id: The id of the newly-created job, or %NULL to use the
|
||||
* device name of @bs.
|
||||
* @bs: Active block device.
|
||||
* @top: Top block device to be committed.
|
||||
* @base: Block device that will be written into, and become the new top.
|
||||
@ -674,12 +679,14 @@ void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
* @errp: Error object.
|
||||
*
|
||||
*/
|
||||
void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
BlockDriverState *top, int64_t speed,
|
||||
BlockdevOnError on_error, BlockCompletionFunc *cb,
|
||||
void *opaque, const char *backing_file_str, Error **errp);
|
||||
void commit_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *base, BlockDriverState *top, int64_t speed,
|
||||
BlockdevOnError on_error, BlockCompletionFunc *cb,
|
||||
void *opaque, const char *backing_file_str, Error **errp);
|
||||
/**
|
||||
* commit_active_start:
|
||||
* @job_id: The id of the newly-created job, or %NULL to use the
|
||||
* device name of @bs.
|
||||
* @bs: Active block device to be committed.
|
||||
* @base: Block device that will be written into, and become the new top.
|
||||
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
|
||||
@ -689,13 +696,15 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
* @errp: Error object.
|
||||
*
|
||||
*/
|
||||
void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
int64_t speed,
|
||||
void commit_active_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *base, int64_t speed,
|
||||
BlockdevOnError on_error,
|
||||
BlockCompletionFunc *cb,
|
||||
void *opaque, Error **errp);
|
||||
/*
|
||||
* mirror_start:
|
||||
* @job_id: The id of the newly-created job, or %NULL to use the
|
||||
* device name of @bs.
|
||||
* @bs: Block device to operate on.
|
||||
* @target: Block device to write to.
|
||||
* @replaces: Block graph node name to replace once the mirror is done. Can
|
||||
@ -717,8 +726,8 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
|
||||
* manually completed. At the end of a successful mirroring job,
|
||||
* @bs will be switched to read from @target.
|
||||
*/
|
||||
void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
const char *replaces,
|
||||
void mirror_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *target, const char *replaces,
|
||||
int64_t speed, uint32_t granularity, int64_t buf_size,
|
||||
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
|
||||
BlockdevOnError on_source_error,
|
||||
@ -729,6 +738,8 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
|
||||
/*
|
||||
* backup_start:
|
||||
* @job_id: The id of the newly-created job, or %NULL to use the
|
||||
* device name of @bs.
|
||||
* @bs: Block device to operate on.
|
||||
* @target: Block device to write to.
|
||||
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
|
||||
@ -743,9 +754,9 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
* Start a backup operation on @bs. Clusters in @bs are written to @target
|
||||
* until the job is cancelled or manually completed.
|
||||
*/
|
||||
void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
||||
int64_t speed, MirrorSyncMode sync_mode,
|
||||
BdrvDirtyBitmap *sync_bitmap,
|
||||
void backup_start(const char *job_id, BlockDriverState *bs,
|
||||
BlockDriverState *target, int64_t speed,
|
||||
MirrorSyncMode sync_mode, BdrvDirtyBitmap *sync_bitmap,
|
||||
BlockdevOnError on_source_error,
|
||||
BlockdevOnError on_target_error,
|
||||
BlockCompletionFunc *cb, void *opaque,
|
||||
|
@ -107,10 +107,7 @@ struct BlockJob {
|
||||
BlockBackend *blk;
|
||||
|
||||
/**
|
||||
* The ID of the block job. Currently the BlockBackend name of the BDS
|
||||
* owning the job at the time when the job is started.
|
||||
*
|
||||
* TODO Decouple block job IDs from BlockBackend names
|
||||
* The ID of the block job.
|
||||
*/
|
||||
char *id;
|
||||
|
||||
@ -214,8 +211,20 @@ struct BlockJob {
|
||||
*/
|
||||
BlockJob *block_job_next(BlockJob *job);
|
||||
|
||||
/**
|
||||
* block_job_get:
|
||||
* @id: The id of the block job.
|
||||
*
|
||||
* Get the block job identified by @id (which must not be %NULL).
|
||||
*
|
||||
* Returns the requested job, or %NULL if it doesn't exist.
|
||||
*/
|
||||
BlockJob *block_job_get(const char *id);
|
||||
|
||||
/**
|
||||
* block_job_create:
|
||||
* @job_id: The id of the newly-created job, or %NULL to have one
|
||||
* generated automatically.
|
||||
* @job_type: The class object for the newly-created job.
|
||||
* @bs: The block
|
||||
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
|
||||
@ -232,9 +241,9 @@ BlockJob *block_job_next(BlockJob *job);
|
||||
* This function is not part of the public job interface; it should be
|
||||
* called from a wrapper that is specific to the job type.
|
||||
*/
|
||||
void *block_job_create(const BlockJobDriver *driver, BlockDriverState *bs,
|
||||
int64_t speed, BlockCompletionFunc *cb,
|
||||
void *opaque, Error **errp);
|
||||
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
||||
BlockDriverState *bs, int64_t speed,
|
||||
BlockCompletionFunc *cb, void *opaque, Error **errp);
|
||||
|
||||
/**
|
||||
* block_job_sleep_ns:
|
||||
|
@ -25,6 +25,9 @@ typedef struct BlockConf {
|
||||
uint32_t discard_granularity;
|
||||
/* geometry, not all devices use this */
|
||||
uint32_t cyls, heads, secs;
|
||||
OnOffAuto wce;
|
||||
BlockdevOnError rerror;
|
||||
BlockdevOnError werror;
|
||||
} BlockConf;
|
||||
|
||||
static inline unsigned int get_physical_block_exp(BlockConf *conf)
|
||||
@ -49,13 +52,20 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
|
||||
DEFINE_PROP_UINT16("min_io_size", _state, _conf.min_io_size, 0), \
|
||||
DEFINE_PROP_UINT32("opt_io_size", _state, _conf.opt_io_size, 0), \
|
||||
DEFINE_PROP_UINT32("discard_granularity", _state, \
|
||||
_conf.discard_granularity, -1)
|
||||
_conf.discard_granularity, -1), \
|
||||
DEFINE_PROP_ON_OFF_AUTO("write-cache", _state, _conf.wce, ON_OFF_AUTO_AUTO)
|
||||
|
||||
#define DEFINE_BLOCK_CHS_PROPERTIES(_state, _conf) \
|
||||
DEFINE_PROP_UINT32("cyls", _state, _conf.cyls, 0), \
|
||||
DEFINE_PROP_UINT32("heads", _state, _conf.heads, 0), \
|
||||
DEFINE_PROP_UINT32("secs", _state, _conf.secs, 0)
|
||||
|
||||
#define DEFINE_BLOCK_ERROR_PROPERTIES(_state, _conf) \
|
||||
DEFINE_PROP_BLOCKDEV_ON_ERROR("rerror", _state, _conf.rerror, \
|
||||
BLOCKDEV_ON_ERROR_AUTO), \
|
||||
DEFINE_PROP_BLOCKDEV_ON_ERROR("werror", _state, _conf.werror, \
|
||||
BLOCKDEV_ON_ERROR_AUTO)
|
||||
|
||||
/* Configuration helpers */
|
||||
|
||||
void blkconf_serial(BlockConf *conf, char **serial);
|
||||
@ -63,6 +73,7 @@ void blkconf_geometry(BlockConf *conf, int *trans,
|
||||
unsigned cyls_max, unsigned heads_max, unsigned secs_max,
|
||||
Error **errp);
|
||||
void blkconf_blocksizes(BlockConf *conf);
|
||||
void blkconf_apply_backend_options(BlockConf *conf);
|
||||
|
||||
/* Hard disk geometry */
|
||||
|
||||
|
@ -20,6 +20,7 @@ extern PropertyInfo qdev_prop_ptr;
|
||||
extern PropertyInfo qdev_prop_macaddr;
|
||||
extern PropertyInfo qdev_prop_on_off_auto;
|
||||
extern PropertyInfo qdev_prop_losttickpolicy;
|
||||
extern PropertyInfo qdev_prop_blockdev_on_error;
|
||||
extern PropertyInfo qdev_prop_bios_chs_trans;
|
||||
extern PropertyInfo qdev_prop_fdc_drive_type;
|
||||
extern PropertyInfo qdev_prop_drive;
|
||||
@ -161,6 +162,9 @@ extern PropertyInfo qdev_prop_arraylen;
|
||||
#define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \
|
||||
DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_losttickpolicy, \
|
||||
LostTickPolicy)
|
||||
#define DEFINE_PROP_BLOCKDEV_ON_ERROR(_n, _s, _f, _d) \
|
||||
DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_blockdev_on_error, \
|
||||
BlockdevOnError)
|
||||
#define DEFINE_PROP_BIOS_CHS_TRANS(_n, _s, _f, _d) \
|
||||
DEFINE_PROP_DEFAULT(_n, _s, _f, _d, qdev_prop_bios_chs_trans, int)
|
||||
#define DEFINE_PROP_BLOCKSIZE(_n, _s, _f) \
|
||||
|
@ -19,9 +19,6 @@
|
||||
#define QERR_BASE_NOT_FOUND \
|
||||
"Base '%s' not found"
|
||||
|
||||
#define QERR_BLOCK_JOB_NOT_READY \
|
||||
"The active block job for device '%s' cannot be completed"
|
||||
|
||||
#define QERR_BUS_NO_HOTPLUG \
|
||||
"Bus '%s' does not support hotplugging"
|
||||
|
||||
|
@ -61,16 +61,14 @@ typedef void coroutine_fn CoroutineEntry(void *opaque);
|
||||
* Create a new coroutine
|
||||
*
|
||||
* Use qemu_coroutine_enter() to actually transfer control to the coroutine.
|
||||
* The opaque argument is passed as the argument to the entry point.
|
||||
*/
|
||||
Coroutine *qemu_coroutine_create(CoroutineEntry *entry);
|
||||
Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque);
|
||||
|
||||
/**
|
||||
* Transfer control to a coroutine
|
||||
*
|
||||
* The opaque argument is passed as the argument to the entry point when
|
||||
* entering the coroutine for the first time. It is subsequently ignored.
|
||||
*/
|
||||
void qemu_coroutine_enter(Coroutine *coroutine, void *opaque);
|
||||
void qemu_coroutine_enter(Coroutine *coroutine);
|
||||
|
||||
/**
|
||||
* Transfer control back to a coroutine's caller
|
||||
@ -102,7 +100,7 @@ bool qemu_in_coroutine(void);
|
||||
* are built.
|
||||
*/
|
||||
typedef struct CoQueue {
|
||||
QTAILQ_HEAD(, Coroutine) entries;
|
||||
QSIMPLEQ_HEAD(, Coroutine) entries;
|
||||
} CoQueue;
|
||||
|
||||
/**
|
||||
|
@ -41,8 +41,8 @@ struct Coroutine {
|
||||
QSLIST_ENTRY(Coroutine) pool_next;
|
||||
|
||||
/* Coroutines that should be woken up when we yield or terminate */
|
||||
QTAILQ_HEAD(, Coroutine) co_queue_wakeup;
|
||||
QTAILQ_ENTRY(Coroutine) co_queue_next;
|
||||
QSIMPLEQ_HEAD(, Coroutine) co_queue_wakeup;
|
||||
QSIMPLEQ_ENTRY(Coroutine) co_queue_next;
|
||||
};
|
||||
|
||||
Coroutine *qemu_coroutine_new(void);
|
||||
|
@ -64,11 +64,11 @@ int qemu_init_main_loop(Error **errp);
|
||||
*
|
||||
* void enter_co_bh(void *opaque) {
|
||||
* QEMUCoroutine *co = opaque;
|
||||
* qemu_coroutine_enter(co, NULL);
|
||||
* qemu_coroutine_enter(co);
|
||||
* }
|
||||
*
|
||||
* ...
|
||||
* QEMUCoroutine *co = qemu_coroutine_create(coroutine_entry);
|
||||
* QEMUCoroutine *co = qemu_coroutine_create(coroutine_entry, NULL);
|
||||
* QEMUBH *start_bh = qemu_bh_new(enter_co_bh, co);
|
||||
* qemu_bh_schedule(start_bh);
|
||||
* while (...) {
|
||||
|
@ -278,6 +278,9 @@ int qemu_madvise(void *addr, size_t len, int advice);
|
||||
|
||||
int qemu_open(const char *name, int flags, ...);
|
||||
int qemu_close(int fd);
|
||||
#ifndef _WIN32
|
||||
int qemu_dup(int fd);
|
||||
#endif
|
||||
|
||||
#if defined(__HAIKU__) && defined(__i386__)
|
||||
#define FMT_pid "%ld"
|
||||
|
@ -15,34 +15,59 @@
|
||||
#define QEMU_RATELIMIT_H
|
||||
|
||||
typedef struct {
|
||||
int64_t next_slice_time;
|
||||
int64_t slice_start_time;
|
||||
int64_t slice_end_time;
|
||||
uint64_t slice_quota;
|
||||
uint64_t slice_ns;
|
||||
uint64_t dispatched;
|
||||
} RateLimit;
|
||||
|
||||
/** Calculate and return delay for next request in ns
|
||||
*
|
||||
* Record that we sent @p n data units. If we may send more data units
|
||||
* in the current time slice, return 0 (i.e. no delay). Otherwise
|
||||
* return the amount of time (in ns) until the start of the next time
|
||||
* slice that will permit sending the next chunk of data.
|
||||
*
|
||||
* Recording sent data units even after exceeding the quota is
|
||||
* permitted; the time slice will be extended accordingly.
|
||||
*/
|
||||
static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n)
|
||||
{
|
||||
int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
|
||||
uint64_t delay_slices;
|
||||
|
||||
if (limit->next_slice_time < now) {
|
||||
limit->next_slice_time = now + limit->slice_ns;
|
||||
assert(limit->slice_quota && limit->slice_ns);
|
||||
|
||||
if (limit->slice_end_time < now) {
|
||||
/* Previous, possibly extended, time slice finished; reset the
|
||||
* accounting. */
|
||||
limit->slice_start_time = now;
|
||||
limit->slice_end_time = now + limit->slice_ns;
|
||||
limit->dispatched = 0;
|
||||
}
|
||||
if (limit->dispatched == 0 || limit->dispatched + n <= limit->slice_quota) {
|
||||
limit->dispatched += n;
|
||||
|
||||
limit->dispatched += n;
|
||||
if (limit->dispatched < limit->slice_quota) {
|
||||
/* We may send further data within the current time slice, no
|
||||
* need to delay the next request. */
|
||||
return 0;
|
||||
} else {
|
||||
limit->dispatched = n;
|
||||
return limit->next_slice_time - now;
|
||||
}
|
||||
|
||||
/* Quota exceeded. Calculate the next time slice we may start
|
||||
* sending data again. */
|
||||
delay_slices = (limit->dispatched + limit->slice_quota - 1) /
|
||||
limit->slice_quota;
|
||||
limit->slice_end_time = limit->slice_start_time +
|
||||
delay_slices * limit->slice_ns;
|
||||
return limit->slice_end_time - now;
|
||||
}
|
||||
|
||||
static inline void ratelimit_set_speed(RateLimit *limit, uint64_t speed,
|
||||
uint64_t slice_ns)
|
||||
{
|
||||
limit->slice_ns = slice_ns;
|
||||
limit->slice_quota = ((double)speed * slice_ns)/1000000000ULL;
|
||||
limit->slice_quota = MAX(((double)speed * slice_ns) / 1000000000ULL, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -218,7 +218,7 @@ static gboolean qio_channel_yield_enter(QIOChannel *ioc,
|
||||
gpointer opaque)
|
||||
{
|
||||
QIOChannelYieldData *data = opaque;
|
||||
qemu_coroutine_enter(data->co, NULL);
|
||||
qemu_coroutine_enter(data->co);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -418,11 +418,11 @@ static void process_incoming_migration_co(void *opaque)
|
||||
|
||||
void migration_fd_process_incoming(QEMUFile *f)
|
||||
{
|
||||
Coroutine *co = qemu_coroutine_create(process_incoming_migration_co);
|
||||
Coroutine *co = qemu_coroutine_create(process_incoming_migration_co, f);
|
||||
|
||||
migrate_decompress_threads_create();
|
||||
qemu_file_set_blocking(f, false);
|
||||
qemu_coroutine_enter(co, f);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2200,12 +2200,31 @@ void hmp_delvm(Monitor *mon, const QDict *qdict)
|
||||
void hmp_info_snapshots(Monitor *mon, const QDict *qdict)
|
||||
{
|
||||
BlockDriverState *bs, *bs1;
|
||||
BdrvNextIterator it1;
|
||||
QEMUSnapshotInfo *sn_tab, *sn;
|
||||
bool no_snapshot = true;
|
||||
int nb_sns, i;
|
||||
int total;
|
||||
int *available_snapshots;
|
||||
int *global_snapshots;
|
||||
AioContext *aio_context;
|
||||
|
||||
typedef struct SnapshotEntry {
|
||||
QEMUSnapshotInfo sn;
|
||||
QTAILQ_ENTRY(SnapshotEntry) next;
|
||||
} SnapshotEntry;
|
||||
|
||||
typedef struct ImageEntry {
|
||||
const char *imagename;
|
||||
QTAILQ_ENTRY(ImageEntry) next;
|
||||
QTAILQ_HEAD(, SnapshotEntry) snapshots;
|
||||
} ImageEntry;
|
||||
|
||||
QTAILQ_HEAD(, ImageEntry) image_list =
|
||||
QTAILQ_HEAD_INITIALIZER(image_list);
|
||||
|
||||
ImageEntry *image_entry, *next_ie;
|
||||
SnapshotEntry *snapshot_entry;
|
||||
|
||||
bs = bdrv_all_find_vmstate_bs();
|
||||
if (!bs) {
|
||||
monitor_printf(mon, "No available block device supports snapshots\n");
|
||||
@ -2222,34 +2241,102 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict)
|
||||
return;
|
||||
}
|
||||
|
||||
if (nb_sns == 0) {
|
||||
for (bs1 = bdrv_first(&it1); bs1; bs1 = bdrv_next(&it1)) {
|
||||
int bs1_nb_sns = 0;
|
||||
ImageEntry *ie;
|
||||
SnapshotEntry *se;
|
||||
AioContext *ctx = bdrv_get_aio_context(bs1);
|
||||
|
||||
aio_context_acquire(ctx);
|
||||
if (bdrv_can_snapshot(bs1)) {
|
||||
sn = NULL;
|
||||
bs1_nb_sns = bdrv_snapshot_list(bs1, &sn);
|
||||
if (bs1_nb_sns > 0) {
|
||||
no_snapshot = false;
|
||||
ie = g_new0(ImageEntry, 1);
|
||||
ie->imagename = bdrv_get_device_name(bs1);
|
||||
QTAILQ_INIT(&ie->snapshots);
|
||||
QTAILQ_INSERT_TAIL(&image_list, ie, next);
|
||||
for (i = 0; i < bs1_nb_sns; i++) {
|
||||
se = g_new0(SnapshotEntry, 1);
|
||||
se->sn = sn[i];
|
||||
QTAILQ_INSERT_TAIL(&ie->snapshots, se, next);
|
||||
}
|
||||
}
|
||||
g_free(sn);
|
||||
}
|
||||
aio_context_release(ctx);
|
||||
}
|
||||
|
||||
if (no_snapshot) {
|
||||
monitor_printf(mon, "There is no snapshot available.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
available_snapshots = g_new0(int, nb_sns);
|
||||
global_snapshots = g_new0(int, nb_sns);
|
||||
total = 0;
|
||||
for (i = 0; i < nb_sns; i++) {
|
||||
if (bdrv_all_find_snapshot(sn_tab[i].id_str, &bs1) == 0) {
|
||||
available_snapshots[total] = i;
|
||||
SnapshotEntry *next_sn;
|
||||
if (bdrv_all_find_snapshot(sn_tab[i].name, &bs1) == 0) {
|
||||
global_snapshots[total] = i;
|
||||
total++;
|
||||
QTAILQ_FOREACH(image_entry, &image_list, next) {
|
||||
QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots,
|
||||
next, next_sn) {
|
||||
if (!strcmp(sn_tab[i].name, snapshot_entry->sn.name)) {
|
||||
QTAILQ_REMOVE(&image_entry->snapshots, snapshot_entry,
|
||||
next);
|
||||
g_free(snapshot_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
monitor_printf(mon, "List of snapshots present on all disks:\n");
|
||||
|
||||
if (total > 0) {
|
||||
bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, NULL);
|
||||
monitor_printf(mon, "\n");
|
||||
for (i = 0; i < total; i++) {
|
||||
sn = &sn_tab[available_snapshots[i]];
|
||||
sn = &sn_tab[global_snapshots[i]];
|
||||
/* The ID is not guaranteed to be the same on all images, so
|
||||
* overwrite it.
|
||||
*/
|
||||
pstrcpy(sn->id_str, sizeof(sn->id_str), "--");
|
||||
bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, sn);
|
||||
monitor_printf(mon, "\n");
|
||||
}
|
||||
} else {
|
||||
monitor_printf(mon, "There is no suitable snapshot available\n");
|
||||
monitor_printf(mon, "None\n");
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH(image_entry, &image_list, next) {
|
||||
if (QTAILQ_EMPTY(&image_entry->snapshots)) {
|
||||
continue;
|
||||
}
|
||||
monitor_printf(mon,
|
||||
"\nList of partial (non-loadable) snapshots on '%s':\n",
|
||||
image_entry->imagename);
|
||||
bdrv_snapshot_dump((fprintf_function)monitor_printf, mon, NULL);
|
||||
monitor_printf(mon, "\n");
|
||||
QTAILQ_FOREACH(snapshot_entry, &image_entry->snapshots, next) {
|
||||
bdrv_snapshot_dump((fprintf_function)monitor_printf, mon,
|
||||
&snapshot_entry->sn);
|
||||
monitor_printf(mon, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
QTAILQ_FOREACH_SAFE(image_entry, &image_list, next, next_ie) {
|
||||
SnapshotEntry *next_sn;
|
||||
QTAILQ_FOREACH_SAFE(snapshot_entry, &image_entry->snapshots, next,
|
||||
next_sn) {
|
||||
g_free(snapshot_entry);
|
||||
}
|
||||
g_free(image_entry);
|
||||
}
|
||||
g_free(sn_tab);
|
||||
g_free(available_snapshots);
|
||||
g_free(global_snapshots);
|
||||
|
||||
}
|
||||
|
||||
|
12
nbd/server.c
12
nbd/server.c
@ -106,7 +106,7 @@ static gboolean nbd_negotiate_continue(QIOChannel *ioc,
|
||||
GIOCondition condition,
|
||||
void *opaque)
|
||||
{
|
||||
qemu_coroutine_enter(opaque, NULL);
|
||||
qemu_coroutine_enter(opaque);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -1230,9 +1230,9 @@ static void nbd_read(void *opaque)
|
||||
NBDClient *client = opaque;
|
||||
|
||||
if (client->recv_coroutine) {
|
||||
qemu_coroutine_enter(client->recv_coroutine, NULL);
|
||||
qemu_coroutine_enter(client->recv_coroutine);
|
||||
} else {
|
||||
qemu_coroutine_enter(qemu_coroutine_create(nbd_trip), client);
|
||||
qemu_coroutine_enter(qemu_coroutine_create(nbd_trip, client));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1240,7 +1240,7 @@ static void nbd_restart_write(void *opaque)
|
||||
{
|
||||
NBDClient *client = opaque;
|
||||
|
||||
qemu_coroutine_enter(client->send_coroutine, NULL);
|
||||
qemu_coroutine_enter(client->send_coroutine);
|
||||
}
|
||||
|
||||
static void nbd_set_handlers(NBDClient *client)
|
||||
@ -1324,6 +1324,6 @@ void nbd_client_new(NBDExport *exp,
|
||||
client->close = close_fn;
|
||||
|
||||
data->client = client;
|
||||
data->co = qemu_coroutine_create(nbd_co_client_start);
|
||||
qemu_coroutine_enter(data->co, data);
|
||||
data->co = qemu_coroutine_create(nbd_co_client_start, data);
|
||||
qemu_coroutine_enter(data->co);
|
||||
}
|
||||
|
@ -664,10 +664,12 @@
|
||||
# @stop: for guest operations, stop the virtual machine;
|
||||
# for jobs, pause the job
|
||||
#
|
||||
# @auto: inherit the error handling policy of the backend (since: 2.7)
|
||||
#
|
||||
# Since: 1.3
|
||||
##
|
||||
{ 'enum': 'BlockdevOnError',
|
||||
'data': ['report', 'ignore', 'enospc', 'stop'] }
|
||||
'data': ['report', 'ignore', 'enospc', 'stop', 'auto'] }
|
||||
|
||||
##
|
||||
# @MirrorSyncMode:
|
||||
@ -713,7 +715,8 @@
|
||||
#
|
||||
# @type: the job type ('stream' for image streaming)
|
||||
#
|
||||
# @device: the block device name
|
||||
# @device: The job identifier. Originally the device name but other
|
||||
# values are allowed since QEMU 2.7
|
||||
#
|
||||
# @len: the maximum progress value
|
||||
#
|
||||
@ -866,6 +869,9 @@
|
||||
##
|
||||
# @DriveBackup
|
||||
#
|
||||
# @job-id: #optional identifier for the newly-created block job. If
|
||||
# omitted, the device name will be used. (Since 2.7)
|
||||
#
|
||||
# @device: the name of the device which should be copied.
|
||||
#
|
||||
# @target: the target of the new image. If the file exists, or if it
|
||||
@ -903,8 +909,8 @@
|
||||
# Since: 1.6
|
||||
##
|
||||
{ 'struct': 'DriveBackup',
|
||||
'data': { 'device': 'str', 'target': 'str', '*format': 'str',
|
||||
'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
|
||||
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
|
||||
'*format': 'str', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
|
||||
'*speed': 'int', '*bitmap': 'str',
|
||||
'*on-source-error': 'BlockdevOnError',
|
||||
'*on-target-error': 'BlockdevOnError' } }
|
||||
@ -912,6 +918,9 @@
|
||||
##
|
||||
# @BlockdevBackup
|
||||
#
|
||||
# @job-id: #optional identifier for the newly-created block job. If
|
||||
# omitted, the device name will be used. (Since 2.7)
|
||||
#
|
||||
# @device: the name of the device which should be copied.
|
||||
#
|
||||
# @target: the name of the backup target device.
|
||||
@ -938,7 +947,7 @@
|
||||
# Since: 2.3
|
||||
##
|
||||
{ 'struct': 'BlockdevBackup',
|
||||
'data': { 'device': 'str', 'target': 'str',
|
||||
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
|
||||
'sync': 'MirrorSyncMode',
|
||||
'*speed': 'int',
|
||||
'*on-source-error': 'BlockdevOnError',
|
||||
@ -1004,6 +1013,9 @@
|
||||
# Live commit of data from overlay image nodes into backing nodes - i.e.,
|
||||
# writes data between 'top' and 'base' into 'base'.
|
||||
#
|
||||
# @job-id: #optional identifier for the newly-created block job. If
|
||||
# omitted, the device name will be used. (Since 2.7)
|
||||
#
|
||||
# @device: the name of the device
|
||||
#
|
||||
# @base: #optional The file name of the backing image to write data into.
|
||||
@ -1055,7 +1067,7 @@
|
||||
#
|
||||
##
|
||||
{ 'command': 'block-commit',
|
||||
'data': { 'device': 'str', '*base': 'str', '*top': 'str',
|
||||
'data': { '*job-id': 'str', 'device': 'str', '*base': 'str', '*top': 'str',
|
||||
'*backing-file': 'str', '*speed': 'int' } }
|
||||
|
||||
##
|
||||
@ -1108,6 +1120,9 @@
|
||||
#
|
||||
# Start mirroring a block device's writes to a new destination.
|
||||
#
|
||||
# @job-id: #optional identifier for the newly-created block job. If
|
||||
# omitted, the device name will be used. (Since 2.7)
|
||||
#
|
||||
# @device: the name of the device whose writes should be mirrored.
|
||||
#
|
||||
# @target: the target of the new image. If the file exists, or if it
|
||||
@ -1160,8 +1175,8 @@
|
||||
# Since 1.3
|
||||
##
|
||||
{ 'command': 'drive-mirror',
|
||||
'data': { 'device': 'str', 'target': 'str', '*format': 'str',
|
||||
'*node-name': 'str', '*replaces': 'str',
|
||||
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
|
||||
'*format': 'str', '*node-name': 'str', '*replaces': 'str',
|
||||
'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
|
||||
'*speed': 'int', '*granularity': 'uint32',
|
||||
'*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
|
||||
@ -1243,6 +1258,9 @@
|
||||
#
|
||||
# Start mirroring a block device's writes to a new destination.
|
||||
#
|
||||
# @job-id: #optional identifier for the newly-created block job. If
|
||||
# omitted, the device name will be used. (Since 2.7)
|
||||
#
|
||||
# @device: the name of the device whose writes should be mirrored.
|
||||
#
|
||||
# @target: the id or node-name of the block device to mirror to. This mustn't be
|
||||
@ -1279,7 +1297,7 @@
|
||||
# Since 2.6
|
||||
##
|
||||
{ 'command': 'blockdev-mirror',
|
||||
'data': { 'device': 'str', 'target': 'str',
|
||||
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
|
||||
'*replaces': 'str',
|
||||
'sync': 'MirrorSyncMode',
|
||||
'*speed': 'int', '*granularity': 'uint32',
|
||||
@ -1413,6 +1431,9 @@
|
||||
# On successful completion the image file is updated to drop the backing file
|
||||
# and the BLOCK_JOB_COMPLETED event is emitted.
|
||||
#
|
||||
# @job-id: #optional identifier for the newly-created block job. If
|
||||
# omitted, the device name will be used. (Since 2.7)
|
||||
#
|
||||
# @device: the device name
|
||||
#
|
||||
# @base: #optional the common backing file name
|
||||
@ -1444,8 +1465,9 @@
|
||||
# Since: 1.1
|
||||
##
|
||||
{ 'command': 'block-stream',
|
||||
'data': { 'device': 'str', '*base': 'str', '*backing-file': 'str',
|
||||
'*speed': 'int', '*on-error': 'BlockdevOnError' } }
|
||||
'data': { '*job-id': 'str', 'device': 'str', '*base': 'str',
|
||||
'*backing-file': 'str', '*speed': 'int',
|
||||
'*on-error': 'BlockdevOnError' } }
|
||||
|
||||
##
|
||||
# @block-job-set-speed:
|
||||
@ -1456,7 +1478,9 @@
|
||||
#
|
||||
# Throttling can be disabled by setting the speed to 0.
|
||||
#
|
||||
# @device: the device name
|
||||
# @device: The job identifier. This used to be a device name (hence
|
||||
# the name of the parameter), but since QEMU 2.7 it can have
|
||||
# other values.
|
||||
#
|
||||
# @speed: the maximum speed, in bytes per second, or 0 for unlimited.
|
||||
# Defaults to 0.
|
||||
@ -1487,7 +1511,9 @@
|
||||
# operation can be started at a later time to finish copying all data from the
|
||||
# backing file.
|
||||
#
|
||||
# @device: the device name
|
||||
# @device: The job identifier. This used to be a device name (hence
|
||||
# the name of the parameter), but since QEMU 2.7 it can have
|
||||
# other values.
|
||||
#
|
||||
# @force: #optional whether to allow cancellation of a paused job (default
|
||||
# false). Since 1.3.
|
||||
@ -1513,7 +1539,9 @@
|
||||
# the operation is actually paused. Cancelling a paused job automatically
|
||||
# resumes it.
|
||||
#
|
||||
# @device: the device name
|
||||
# @device: The job identifier. This used to be a device name (hence
|
||||
# the name of the parameter), but since QEMU 2.7 it can have
|
||||
# other values.
|
||||
#
|
||||
# Returns: Nothing on success
|
||||
# If no background operation is active on this device, DeviceNotActive
|
||||
@ -1533,7 +1561,9 @@
|
||||
#
|
||||
# This command also clears the error status of the job.
|
||||
#
|
||||
# @device: the device name
|
||||
# @device: The job identifier. This used to be a device name (hence
|
||||
# the name of the parameter), but since QEMU 2.7 it can have
|
||||
# other values.
|
||||
#
|
||||
# Returns: Nothing on success
|
||||
# If no background operation is active on this device, DeviceNotActive
|
||||
@ -1559,7 +1589,9 @@
|
||||
#
|
||||
# A cancelled or paused job cannot be completed.
|
||||
#
|
||||
# @device: the device name
|
||||
# @device: The job identifier. This used to be a device name (hence
|
||||
# the name of the parameter), but since QEMU 2.7 it can have
|
||||
# other values.
|
||||
#
|
||||
# Returns: Nothing on success
|
||||
# If no background operation is active on this device, DeviceNotActive
|
||||
@ -2049,20 +2081,8 @@
|
||||
# @discard: #optional discard-related options (default: ignore)
|
||||
# @cache: #optional cache-related options
|
||||
# @aio: #optional AIO backend (default: threads)
|
||||
# @rerror: #optional how to handle read errors on the device
|
||||
# (default: report)
|
||||
# @werror: #optional how to handle write errors on the device
|
||||
# (default: enospc)
|
||||
# @read-only: #optional whether the block device should be read-only
|
||||
# (default: false)
|
||||
# @stats-account-invalid: #optional whether to include invalid
|
||||
# operations when computing last access statistics
|
||||
# (default: true) (Since 2.5)
|
||||
# @stats-account-failed: #optional whether to include failed
|
||||
# operations when computing latency and last
|
||||
# access statistics (default: true) (Since 2.5)
|
||||
# @stats-intervals: #optional list of intervals for collecting I/O
|
||||
# statistics, in seconds (default: none) (Since 2.5)
|
||||
# @detect-zeroes: #optional detect and optimize zero writes (Since 2.1)
|
||||
# (default: off)
|
||||
#
|
||||
@ -2072,17 +2092,13 @@
|
||||
##
|
||||
{ 'union': 'BlockdevOptions',
|
||||
'base': { 'driver': 'BlockdevDriver',
|
||||
# TODO 'id' is a BB-level option, remove it
|
||||
'*id': 'str',
|
||||
'*node-name': 'str',
|
||||
'*discard': 'BlockdevDiscardOptions',
|
||||
'*cache': 'BlockdevCacheOptions',
|
||||
'*aio': 'BlockdevAioOptions',
|
||||
'*rerror': 'BlockdevOnError',
|
||||
'*werror': 'BlockdevOnError',
|
||||
'*read-only': 'bool',
|
||||
'*stats-account-invalid': 'bool',
|
||||
'*stats-account-failed': 'bool',
|
||||
'*stats-intervals': ['int'],
|
||||
'*detect-zeroes': 'BlockdevDetectZeroesOptions' },
|
||||
'discriminator': 'driver',
|
||||
'data': {
|
||||
@ -2405,7 +2421,8 @@
|
||||
#
|
||||
# @type: job type
|
||||
#
|
||||
# @device: device name
|
||||
# @device: The job identifier. Originally the device name but other
|
||||
# values are allowed since QEMU 2.7
|
||||
#
|
||||
# @len: maximum progress value
|
||||
#
|
||||
@ -2436,7 +2453,8 @@
|
||||
#
|
||||
# @type: job type
|
||||
#
|
||||
# @device: device name
|
||||
# @device: The job identifier. Originally the device name but other
|
||||
# values are allowed since QEMU 2.7
|
||||
#
|
||||
# @len: maximum progress value
|
||||
#
|
||||
@ -2459,7 +2477,8 @@
|
||||
#
|
||||
# Emitted when a block job encounters an error
|
||||
#
|
||||
# @device: device name
|
||||
# @device: The job identifier. Originally the device name but other
|
||||
# values are allowed since QEMU 2.7
|
||||
#
|
||||
# @operation: I/O operation
|
||||
#
|
||||
@ -2479,7 +2498,8 @@
|
||||
#
|
||||
# @type: job type
|
||||
#
|
||||
# @device: device name
|
||||
# @device: The job identifier. Originally the device name but other
|
||||
# values are allowed since QEMU 2.7
|
||||
#
|
||||
# @len: maximum progress value
|
||||
#
|
||||
|
@ -920,7 +920,7 @@ static int img_commit(int argc, char **argv)
|
||||
.bs = bs,
|
||||
};
|
||||
|
||||
commit_active_start(bs, base_bs, 0, BLOCKDEV_ON_ERROR_REPORT,
|
||||
commit_active_start("commit", bs, base_bs, 0, BLOCKDEV_ON_ERROR_REPORT,
|
||||
common_block_job_cb, &cbi, &local_err);
|
||||
if (local_err) {
|
||||
goto done;
|
||||
@ -3283,7 +3283,7 @@ static int img_resize(int argc, char **argv)
|
||||
error_report("Image is read-only");
|
||||
break;
|
||||
default:
|
||||
error_report("Error resizing image (%d)", -ret);
|
||||
error_report("Error resizing image: %s", strerror(-ret));
|
||||
break;
|
||||
}
|
||||
out:
|
||||
|
@ -389,9 +389,9 @@ create_iovec(BlockBackend *blk, QEMUIOVector *qiov, char **argv, int nr_iov,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* should be SIZE_T_MAX, but that doesn't exist */
|
||||
if (len > INT_MAX) {
|
||||
printf("Argument '%s' exceeds maximum size %d\n", arg, INT_MAX);
|
||||
if (len > SIZE_MAX) {
|
||||
printf("Argument '%s' exceeds maximum size %llu\n", arg,
|
||||
(unsigned long long)SIZE_MAX);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -479,12 +479,12 @@ static int do_co_pwrite_zeroes(BlockBackend *blk, int64_t offset,
|
||||
.done = false,
|
||||
};
|
||||
|
||||
if (count >> BDRV_SECTOR_BITS > INT_MAX) {
|
||||
if (count > INT_MAX) {
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
co = qemu_coroutine_create(co_pwrite_zeroes_entry);
|
||||
qemu_coroutine_enter(co, &data);
|
||||
co = qemu_coroutine_create(co_pwrite_zeroes_entry, &data);
|
||||
qemu_coroutine_enter(co);
|
||||
while (!data.done) {
|
||||
aio_poll(blk_get_aio_context(blk), true);
|
||||
}
|
||||
@ -500,7 +500,7 @@ static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset,
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (count >> 9 > INT_MAX) {
|
||||
if (count >> 9 > BDRV_REQUEST_MAX_SECTORS) {
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
@ -1688,9 +1688,9 @@ static int discard_f(BlockBackend *blk, int argc, char **argv)
|
||||
if (count < 0) {
|
||||
print_cvtnum_err(count, argv[optind]);
|
||||
return 0;
|
||||
} else if (count >> BDRV_SECTOR_BITS > INT_MAX) {
|
||||
} else if (count >> BDRV_SECTOR_BITS > BDRV_REQUEST_MAX_SECTORS) {
|
||||
printf("length cannot exceed %"PRIu64", given %s\n",
|
||||
(uint64_t)INT_MAX << BDRV_SECTOR_BITS,
|
||||
(uint64_t)BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS,
|
||||
argv[optind]);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1106,7 +1106,7 @@ EQMP
|
||||
|
||||
{
|
||||
.name = "block-stream",
|
||||
.args_type = "device:B,base:s?,speed:o?,backing-file:s?,on-error:s?",
|
||||
.args_type = "job-id:s?,device:B,base:s?,speed:o?,backing-file:s?,on-error:s?",
|
||||
.mhandler.cmd_new = qmp_marshal_block_stream,
|
||||
},
|
||||
|
||||
@ -1118,6 +1118,8 @@ Copy data from a backing file into a block device.
|
||||
|
||||
Arguments:
|
||||
|
||||
- "job-id": Identifier for the newly-created block job. If omitted,
|
||||
the device name will be used. (json-string, optional)
|
||||
- "device": The device's ID, must be unique (json-string)
|
||||
- "base": The file name of the backing image above which copying starts
|
||||
(json-string, optional)
|
||||
@ -1149,7 +1151,7 @@ EQMP
|
||||
|
||||
{
|
||||
.name = "block-commit",
|
||||
.args_type = "device:B,base:s?,top:s?,backing-file:s?,speed:o?",
|
||||
.args_type = "job-id:s?,device:B,base:s?,top:s?,backing-file:s?,speed:o?",
|
||||
.mhandler.cmd_new = qmp_marshal_block_commit,
|
||||
},
|
||||
|
||||
@ -1162,6 +1164,8 @@ data between 'top' and 'base' into 'base'.
|
||||
|
||||
Arguments:
|
||||
|
||||
- "job-id": Identifier for the newly-created block job. If omitted,
|
||||
the device name will be used. (json-string, optional)
|
||||
- "device": The device's ID, must be unique (json-string)
|
||||
- "base": The file name of the backing image to write data into.
|
||||
If not specified, this is the deepest backing image
|
||||
@ -1212,8 +1216,8 @@ EQMP
|
||||
|
||||
{
|
||||
.name = "drive-backup",
|
||||
.args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
|
||||
"bitmap:s?,on-source-error:s?,on-target-error:s?",
|
||||
.args_type = "job-id:s?,sync:s,device:B,target:s,speed:i?,mode:s?,"
|
||||
"format:s?,bitmap:s?,on-source-error:s?,on-target-error:s?",
|
||||
.mhandler.cmd_new = qmp_marshal_drive_backup,
|
||||
},
|
||||
|
||||
@ -1229,6 +1233,8 @@ block-job-cancel command.
|
||||
|
||||
Arguments:
|
||||
|
||||
- "job-id": Identifier for the newly-created block job. If omitted,
|
||||
the device name will be used. (json-string, optional)
|
||||
- "device": the name of the device which should be copied.
|
||||
(json-string)
|
||||
- "target": the target of the new image. If the file exists, or if it is a
|
||||
@ -1266,7 +1272,7 @@ EQMP
|
||||
|
||||
{
|
||||
.name = "blockdev-backup",
|
||||
.args_type = "sync:s,device:B,target:B,speed:i?,"
|
||||
.args_type = "job-id:s?,sync:s,device:B,target:B,speed:i?,"
|
||||
"on-source-error:s?,on-target-error:s?",
|
||||
.mhandler.cmd_new = qmp_marshal_blockdev_backup,
|
||||
},
|
||||
@ -1280,6 +1286,8 @@ as backup target.
|
||||
|
||||
Arguments:
|
||||
|
||||
- "job-id": Identifier for the newly-created block job. If omitted,
|
||||
the device name will be used. (json-string, optional)
|
||||
- "device": the name of the device which should be copied.
|
||||
(json-string)
|
||||
- "target": the name of the backup target device. (json-string)
|
||||
@ -1656,8 +1664,8 @@ EQMP
|
||||
|
||||
{
|
||||
.name = "drive-mirror",
|
||||
.args_type = "sync:s,device:B,target:s,speed:i?,mode:s?,format:s?,"
|
||||
"node-name:s?,replaces:s?,"
|
||||
.args_type = "job-id:s?,sync:s,device:B,target:s,speed:i?,mode:s?,"
|
||||
"format:s?,node-name:s?,replaces:s?,"
|
||||
"on-source-error:s?,on-target-error:s?,"
|
||||
"unmap:b?,"
|
||||
"granularity:i?,buf-size:i?",
|
||||
@ -1677,6 +1685,8 @@ of the source.
|
||||
|
||||
Arguments:
|
||||
|
||||
- "job-id": Identifier for the newly-created block job. If omitted,
|
||||
the device name will be used. (json-string, optional)
|
||||
- "device": device name to operate on (json-string)
|
||||
- "target": name of new image file (json-string)
|
||||
- "format": format of new image (json-string, optional)
|
||||
@ -1720,7 +1730,7 @@ EQMP
|
||||
|
||||
{
|
||||
.name = "blockdev-mirror",
|
||||
.args_type = "sync:s,device:B,target:B,replaces:s?,speed:i?,"
|
||||
.args_type = "job-id:s?,sync:s,device:B,target:B,replaces:s?,speed:i?,"
|
||||
"on-source-error:s?,on-target-error:s?,"
|
||||
"granularity:i?,buf-size:i?",
|
||||
.mhandler.cmd_new = qmp_marshal_blockdev_mirror,
|
||||
@ -1735,6 +1745,8 @@ specifies the target of mirror operation.
|
||||
|
||||
Arguments:
|
||||
|
||||
- "job-id": Identifier for the newly-created block job. If omitted,
|
||||
the device name will be used. (json-string, optional)
|
||||
- "device": device name to operate on (json-string)
|
||||
- "target": device name to mirror to (json-string)
|
||||
- "replaces": the block driver node name to replace when finished
|
||||
|
@ -184,5 +184,103 @@ class ThrottleTestCase(iotests.QMPTestCase):
|
||||
class ThrottleTestCoroutine(ThrottleTestCase):
|
||||
test_img = "null-co://"
|
||||
|
||||
class ThrottleTestGroupNames(iotests.QMPTestCase):
|
||||
test_img = "null-aio://"
|
||||
max_drives = 3
|
||||
|
||||
def setUp(self):
|
||||
self.vm = iotests.VM()
|
||||
for i in range(0, self.max_drives):
|
||||
self.vm.add_drive(self.test_img, "throttling.iops-total=100")
|
||||
self.vm.launch()
|
||||
|
||||
def tearDown(self):
|
||||
self.vm.shutdown()
|
||||
|
||||
def set_io_throttle(self, device, params):
|
||||
params["device"] = device
|
||||
result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params)
|
||||
self.assert_qmp(result, 'return', {})
|
||||
|
||||
def verify_name(self, device, name):
|
||||
result = self.vm.qmp("query-block")
|
||||
for r in result["return"]:
|
||||
if r["device"] == device:
|
||||
info = r["inserted"]
|
||||
if name:
|
||||
self.assertEqual(info["group"], name)
|
||||
else:
|
||||
self.assertFalse(info.has_key('group'))
|
||||
return
|
||||
|
||||
raise Exception("No group information found for '%s'" % device)
|
||||
|
||||
def test_group_naming(self):
|
||||
params = {"bps": 0,
|
||||
"bps_rd": 0,
|
||||
"bps_wr": 0,
|
||||
"iops": 0,
|
||||
"iops_rd": 0,
|
||||
"iops_wr": 0}
|
||||
|
||||
# Check the drives added using the command line.
|
||||
# The default throttling group name is the device name.
|
||||
for i in range(self.max_drives):
|
||||
devname = "drive%d" % i
|
||||
self.verify_name(devname, devname)
|
||||
|
||||
# Clear throttling settings => the group name is gone.
|
||||
for i in range(self.max_drives):
|
||||
devname = "drive%d" % i
|
||||
self.set_io_throttle(devname, params)
|
||||
self.verify_name(devname, None)
|
||||
|
||||
# Set throttling settings using block_set_io_throttle and
|
||||
# check the default group names.
|
||||
params["iops"] = 10
|
||||
for i in range(self.max_drives):
|
||||
devname = "drive%d" % i
|
||||
self.set_io_throttle(devname, params)
|
||||
self.verify_name(devname, devname)
|
||||
|
||||
# Set a custom group name for each device
|
||||
for i in range(3):
|
||||
devname = "drive%d" % i
|
||||
groupname = "group%d" % i
|
||||
params['group'] = groupname
|
||||
self.set_io_throttle(devname, params)
|
||||
self.verify_name(devname, groupname)
|
||||
|
||||
# Put drive0 in group1 and check that all other devices remain
|
||||
# unchanged
|
||||
params['group'] = 'group1'
|
||||
self.set_io_throttle('drive0', params)
|
||||
self.verify_name('drive0', 'group1')
|
||||
for i in range(1, self.max_drives):
|
||||
devname = "drive%d" % i
|
||||
groupname = "group%d" % i
|
||||
self.verify_name(devname, groupname)
|
||||
|
||||
# Put drive0 in group2 and check that all other devices remain
|
||||
# unchanged
|
||||
params['group'] = 'group2'
|
||||
self.set_io_throttle('drive0', params)
|
||||
self.verify_name('drive0', 'group2')
|
||||
for i in range(1, self.max_drives):
|
||||
devname = "drive%d" % i
|
||||
groupname = "group%d" % i
|
||||
self.verify_name(devname, groupname)
|
||||
|
||||
# Clear throttling settings from drive0 check that all other
|
||||
# devices remain unchanged
|
||||
params["iops"] = 0
|
||||
self.set_io_throttle('drive0', params)
|
||||
self.verify_name('drive0', None)
|
||||
for i in range(1, self.max_drives):
|
||||
devname = "drive%d" % i
|
||||
groupname = "group%d" % i
|
||||
self.verify_name(devname, groupname)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
iotests.main(supported_fmts=["raw"])
|
||||
|
@ -1,5 +1,5 @@
|
||||
....
|
||||
.....
|
||||
----------------------------------------------------------------------
|
||||
Ran 4 tests
|
||||
Ran 5 tests
|
||||
|
||||
OK
|
||||
|
89
tests/qemu-iotests/157
Executable file
89
tests/qemu-iotests/157
Executable file
@ -0,0 +1,89 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Test command line configuration of block devices with qdev
|
||||
#
|
||||
# Copyright (C) 2016 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"
|
||||
status=1 # failure is the default!
|
||||
|
||||
_cleanup()
|
||||
{
|
||||
_cleanup_test_img
|
||||
}
|
||||
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||
|
||||
# get standard environment, filters and checks
|
||||
. ./common.rc
|
||||
. ./common.filter
|
||||
|
||||
_supported_fmt generic
|
||||
_supported_proto file
|
||||
_supported_os Linux
|
||||
|
||||
function do_run_qemu()
|
||||
{
|
||||
echo Testing: "$@"
|
||||
(
|
||||
if ! test -t 0; then
|
||||
while read cmd; do
|
||||
echo $cmd
|
||||
done
|
||||
fi
|
||||
echo quit
|
||||
) | $QEMU -nodefaults -nographic -monitor stdio -serial none "$@"
|
||||
echo
|
||||
}
|
||||
|
||||
function run_qemu()
|
||||
{
|
||||
do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_imgfmt \
|
||||
| _filter_qemu | _filter_generated_node_ids
|
||||
}
|
||||
|
||||
|
||||
size=128M
|
||||
drive="if=none,file=$TEST_IMG,driver=$IMGFMT"
|
||||
|
||||
_make_test_img $size
|
||||
|
||||
echo
|
||||
echo "=== Setting WCE with qdev and with manually created BB ==="
|
||||
echo
|
||||
|
||||
# The qdev option takes precedence, but if it isn't given or 'auto', the BB
|
||||
# option is used instead.
|
||||
|
||||
for cache in "writeback" "writethrough"; do
|
||||
for wce in "" ",write-cache=auto" ",write-cache=on" ",write-cache=off"; do
|
||||
echo "info block" \
|
||||
| run_qemu -drive "$drive,cache=$cache" \
|
||||
-device "virtio-blk,drive=none0$wce" \
|
||||
| grep -e "Testing" -e "Cache mode"
|
||||
done
|
||||
done
|
||||
|
||||
# success, all done
|
||||
echo "*** done"
|
||||
rm -f $seq.full
|
||||
status=0
|
22
tests/qemu-iotests/157.out
Normal file
22
tests/qemu-iotests/157.out
Normal file
@ -0,0 +1,22 @@
|
||||
QA output created by 157
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
||||
|
||||
=== Setting WCE with qdev and with manually created BB ===
|
||||
|
||||
Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,driver=IMGFMT,cache=writeback -device virtio-blk,drive=none0
|
||||
Cache mode: writeback
|
||||
Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,driver=IMGFMT,cache=writeback -device virtio-blk,drive=none0,write-cache=auto
|
||||
Cache mode: writeback
|
||||
Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,driver=IMGFMT,cache=writeback -device virtio-blk,drive=none0,write-cache=on
|
||||
Cache mode: writeback
|
||||
Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,driver=IMGFMT,cache=writeback -device virtio-blk,drive=none0,write-cache=off
|
||||
Cache mode: writethrough
|
||||
Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,driver=IMGFMT,cache=writethrough -device virtio-blk,drive=none0
|
||||
Cache mode: writethrough
|
||||
Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,driver=IMGFMT,cache=writethrough -device virtio-blk,drive=none0,write-cache=auto
|
||||
Cache mode: writethrough
|
||||
Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,driver=IMGFMT,cache=writethrough -device virtio-blk,drive=none0,write-cache=on
|
||||
Cache mode: writeback
|
||||
Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,driver=IMGFMT,cache=writethrough -device virtio-blk,drive=none0,write-cache=off
|
||||
Cache mode: writethrough
|
||||
*** done
|
@ -156,3 +156,4 @@
|
||||
154 rw auto backing quick
|
||||
155 rw auto
|
||||
156 rw auto quick
|
||||
157 auto
|
||||
|
@ -91,19 +91,22 @@ static BlockJob *test_block_job_start(unsigned int iterations,
|
||||
BlockDriverState *bs;
|
||||
TestBlockJob *s;
|
||||
TestBlockJobCBData *data;
|
||||
static unsigned counter;
|
||||
char job_id[24];
|
||||
|
||||
data = g_new0(TestBlockJobCBData, 1);
|
||||
bs = bdrv_new();
|
||||
s = block_job_create(&test_block_job_driver, bs, 0, test_block_job_cb,
|
||||
data, &error_abort);
|
||||
snprintf(job_id, sizeof(job_id), "job%u", counter++);
|
||||
s = block_job_create(job_id, &test_block_job_driver, bs, 0,
|
||||
test_block_job_cb, data, &error_abort);
|
||||
s->iterations = iterations;
|
||||
s->use_timer = use_timer;
|
||||
s->rc = rc;
|
||||
s->result = result;
|
||||
s->common.co = qemu_coroutine_create(test_block_job_run);
|
||||
s->common.co = qemu_coroutine_create(test_block_job_run, s);
|
||||
data->job = s;
|
||||
data->result = result;
|
||||
qemu_coroutine_enter(s->common.co, s);
|
||||
qemu_coroutine_enter(s->common.co);
|
||||
return &s->common;
|
||||
}
|
||||
|
||||
|
@ -30,8 +30,8 @@ static void test_in_coroutine(void)
|
||||
|
||||
g_assert(!qemu_in_coroutine());
|
||||
|
||||
coroutine = qemu_coroutine_create(verify_in_coroutine);
|
||||
qemu_coroutine_enter(coroutine, NULL);
|
||||
coroutine = qemu_coroutine_create(verify_in_coroutine, NULL);
|
||||
qemu_coroutine_enter(coroutine);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -40,15 +40,16 @@ static void test_in_coroutine(void)
|
||||
|
||||
static void coroutine_fn verify_self(void *opaque)
|
||||
{
|
||||
g_assert(qemu_coroutine_self() == opaque);
|
||||
Coroutine **p_co = opaque;
|
||||
g_assert(qemu_coroutine_self() == *p_co);
|
||||
}
|
||||
|
||||
static void test_self(void)
|
||||
{
|
||||
Coroutine *coroutine;
|
||||
|
||||
coroutine = qemu_coroutine_create(verify_self);
|
||||
qemu_coroutine_enter(coroutine, coroutine);
|
||||
coroutine = qemu_coroutine_create(verify_self, &coroutine);
|
||||
qemu_coroutine_enter(coroutine);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -70,8 +71,8 @@ static void coroutine_fn nest(void *opaque)
|
||||
if (nd->n_enter < nd->max) {
|
||||
Coroutine *child;
|
||||
|
||||
child = qemu_coroutine_create(nest);
|
||||
qemu_coroutine_enter(child, nd);
|
||||
child = qemu_coroutine_create(nest, nd);
|
||||
qemu_coroutine_enter(child);
|
||||
}
|
||||
|
||||
nd->n_return++;
|
||||
@ -86,8 +87,8 @@ static void test_nesting(void)
|
||||
.max = 128,
|
||||
};
|
||||
|
||||
root = qemu_coroutine_create(nest);
|
||||
qemu_coroutine_enter(root, &nd);
|
||||
root = qemu_coroutine_create(nest, &nd);
|
||||
qemu_coroutine_enter(root);
|
||||
|
||||
/* Must enter and return from max nesting level */
|
||||
g_assert_cmpint(nd.n_enter, ==, nd.max);
|
||||
@ -115,9 +116,9 @@ static void test_yield(void)
|
||||
bool done = false;
|
||||
int i = -1; /* one extra time to return from coroutine */
|
||||
|
||||
coroutine = qemu_coroutine_create(yield_5_times);
|
||||
coroutine = qemu_coroutine_create(yield_5_times, &done);
|
||||
while (!done) {
|
||||
qemu_coroutine_enter(coroutine, &done);
|
||||
qemu_coroutine_enter(coroutine);
|
||||
i++;
|
||||
}
|
||||
g_assert_cmpint(i, ==, 5); /* coroutine must yield 5 times */
|
||||
@ -131,7 +132,7 @@ static void coroutine_fn c2_fn(void *opaque)
|
||||
static void coroutine_fn c1_fn(void *opaque)
|
||||
{
|
||||
Coroutine *c2 = opaque;
|
||||
qemu_coroutine_enter(c2, NULL);
|
||||
qemu_coroutine_enter(c2);
|
||||
}
|
||||
|
||||
static void test_co_queue(void)
|
||||
@ -139,12 +140,12 @@ static void test_co_queue(void)
|
||||
Coroutine *c1;
|
||||
Coroutine *c2;
|
||||
|
||||
c1 = qemu_coroutine_create(c1_fn);
|
||||
c2 = qemu_coroutine_create(c2_fn);
|
||||
c2 = qemu_coroutine_create(c2_fn, NULL);
|
||||
c1 = qemu_coroutine_create(c1_fn, c2);
|
||||
|
||||
qemu_coroutine_enter(c1, c2);
|
||||
qemu_coroutine_enter(c1);
|
||||
memset(c1, 0xff, sizeof(Coroutine));
|
||||
qemu_coroutine_enter(c2, NULL);
|
||||
qemu_coroutine_enter(c2);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -164,14 +165,14 @@ static void test_lifecycle(void)
|
||||
bool done = false;
|
||||
|
||||
/* Create, enter, and return from coroutine */
|
||||
coroutine = qemu_coroutine_create(set_and_exit);
|
||||
qemu_coroutine_enter(coroutine, &done);
|
||||
coroutine = qemu_coroutine_create(set_and_exit, &done);
|
||||
qemu_coroutine_enter(coroutine);
|
||||
g_assert(done); /* expect done to be true (first time) */
|
||||
|
||||
/* Repeat to check that no state affects this test */
|
||||
done = false;
|
||||
coroutine = qemu_coroutine_create(set_and_exit);
|
||||
qemu_coroutine_enter(coroutine, &done);
|
||||
coroutine = qemu_coroutine_create(set_and_exit, &done);
|
||||
qemu_coroutine_enter(coroutine);
|
||||
g_assert(done); /* expect done to be true (second time) */
|
||||
}
|
||||
|
||||
@ -205,12 +206,12 @@ static void do_order_test(void)
|
||||
{
|
||||
Coroutine *co;
|
||||
|
||||
co = qemu_coroutine_create(co_order_test);
|
||||
co = qemu_coroutine_create(co_order_test, NULL);
|
||||
record_push(1, 1);
|
||||
qemu_coroutine_enter(co, NULL);
|
||||
qemu_coroutine_enter(co);
|
||||
record_push(1, 2);
|
||||
g_assert(!qemu_in_coroutine());
|
||||
qemu_coroutine_enter(co, NULL);
|
||||
qemu_coroutine_enter(co);
|
||||
record_push(1, 3);
|
||||
g_assert(!qemu_in_coroutine());
|
||||
}
|
||||
@ -247,8 +248,8 @@ static void perf_lifecycle(void)
|
||||
|
||||
g_test_timer_start();
|
||||
for (i = 0; i < max; i++) {
|
||||
coroutine = qemu_coroutine_create(empty_coroutine);
|
||||
qemu_coroutine_enter(coroutine, NULL);
|
||||
coroutine = qemu_coroutine_create(empty_coroutine, NULL);
|
||||
qemu_coroutine_enter(coroutine);
|
||||
}
|
||||
duration = g_test_timer_elapsed();
|
||||
|
||||
@ -271,8 +272,8 @@ static void perf_nesting(void)
|
||||
.n_return = 0,
|
||||
.max = maxnesting,
|
||||
};
|
||||
root = qemu_coroutine_create(nest);
|
||||
qemu_coroutine_enter(root, &nd);
|
||||
root = qemu_coroutine_create(nest, &nd);
|
||||
qemu_coroutine_enter(root);
|
||||
}
|
||||
duration = g_test_timer_elapsed();
|
||||
|
||||
@ -301,11 +302,11 @@ static void perf_yield(void)
|
||||
|
||||
maxcycles = 100000000;
|
||||
i = maxcycles;
|
||||
Coroutine *coroutine = qemu_coroutine_create(yield_loop);
|
||||
Coroutine *coroutine = qemu_coroutine_create(yield_loop, &i);
|
||||
|
||||
g_test_timer_start();
|
||||
while (i > 0) {
|
||||
qemu_coroutine_enter(coroutine, &i);
|
||||
qemu_coroutine_enter(coroutine);
|
||||
}
|
||||
duration = g_test_timer_elapsed();
|
||||
|
||||
@ -351,9 +352,9 @@ static void perf_cost(void)
|
||||
|
||||
g_test_timer_start();
|
||||
while (i++ < maxcycles) {
|
||||
co = qemu_coroutine_create(perf_cost_func);
|
||||
qemu_coroutine_enter(co, &i);
|
||||
qemu_coroutine_enter(co, NULL);
|
||||
co = qemu_coroutine_create(perf_cost_func, &i);
|
||||
qemu_coroutine_enter(co);
|
||||
qemu_coroutine_enter(co);
|
||||
}
|
||||
duration = g_test_timer_elapsed();
|
||||
ops = (long)(maxcycles / (duration * 1000));
|
||||
|
@ -91,9 +91,9 @@ static void co_test_cb(void *opaque)
|
||||
static void test_submit_co(void)
|
||||
{
|
||||
WorkerTestData data;
|
||||
Coroutine *co = qemu_coroutine_create(co_test_cb);
|
||||
Coroutine *co = qemu_coroutine_create(co_test_cb, &data);
|
||||
|
||||
qemu_coroutine_enter(co, &data);
|
||||
qemu_coroutine_enter(co);
|
||||
|
||||
/* Back here once the worker has started. */
|
||||
|
||||
|
@ -267,7 +267,7 @@ static void thread_pool_co_cb(void *opaque, int ret)
|
||||
ThreadPoolCo *co = opaque;
|
||||
|
||||
co->ret = ret;
|
||||
qemu_coroutine_enter(co->co, NULL);
|
||||
qemu_coroutine_enter(co->co);
|
||||
}
|
||||
|
||||
int coroutine_fn thread_pool_submit_co(ThreadPool *pool, ThreadPoolFunc *func,
|
||||
|
23
util/osdep.c
23
util/osdep.c
@ -83,14 +83,7 @@ static int qemu_dup_flags(int fd, int flags)
|
||||
int serrno;
|
||||
int dup_flags;
|
||||
|
||||
#ifdef F_DUPFD_CLOEXEC
|
||||
ret = fcntl(fd, F_DUPFD_CLOEXEC, 0);
|
||||
#else
|
||||
ret = dup(fd);
|
||||
if (ret != -1) {
|
||||
qemu_set_cloexec(ret);
|
||||
}
|
||||
#endif
|
||||
ret = qemu_dup(fd);
|
||||
if (ret == -1) {
|
||||
goto fail;
|
||||
}
|
||||
@ -129,6 +122,20 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
int qemu_dup(int fd)
|
||||
{
|
||||
int ret;
|
||||
#ifdef F_DUPFD_CLOEXEC
|
||||
ret = fcntl(fd, F_DUPFD_CLOEXEC, 0);
|
||||
#else
|
||||
ret = dup(fd);
|
||||
if (ret != -1) {
|
||||
qemu_set_cloexec(ret);
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int qemu_parse_fdset(const char *param)
|
||||
{
|
||||
return qemu_parse_fd(param);
|
||||
|
@ -75,7 +75,7 @@ static void fd_coroutine_enter(void *opaque)
|
||||
{
|
||||
FDYieldUntilData *data = opaque;
|
||||
qemu_set_fd_handler(data->fd, NULL, NULL, NULL);
|
||||
qemu_coroutine_enter(data->co, NULL);
|
||||
qemu_coroutine_enter(data->co);
|
||||
}
|
||||
|
||||
void coroutine_fn yield_until_fd_readable(int fd)
|
||||
|
@ -31,13 +31,13 @@
|
||||
|
||||
void qemu_co_queue_init(CoQueue *queue)
|
||||
{
|
||||
QTAILQ_INIT(&queue->entries);
|
||||
QSIMPLEQ_INIT(&queue->entries);
|
||||
}
|
||||
|
||||
void coroutine_fn qemu_co_queue_wait(CoQueue *queue)
|
||||
{
|
||||
Coroutine *self = qemu_coroutine_self();
|
||||
QTAILQ_INSERT_TAIL(&queue->entries, self, co_queue_next);
|
||||
QSIMPLEQ_INSERT_TAIL(&queue->entries, self, co_queue_next);
|
||||
qemu_coroutine_yield();
|
||||
assert(qemu_in_coroutine());
|
||||
}
|
||||
@ -55,9 +55,9 @@ void qemu_co_queue_run_restart(Coroutine *co)
|
||||
Coroutine *next;
|
||||
|
||||
trace_qemu_co_queue_run_restart(co);
|
||||
while ((next = QTAILQ_FIRST(&co->co_queue_wakeup))) {
|
||||
QTAILQ_REMOVE(&co->co_queue_wakeup, next, co_queue_next);
|
||||
qemu_coroutine_enter(next, NULL);
|
||||
while ((next = QSIMPLEQ_FIRST(&co->co_queue_wakeup))) {
|
||||
QSIMPLEQ_REMOVE_HEAD(&co->co_queue_wakeup, co_queue_next);
|
||||
qemu_coroutine_enter(next);
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,13 +66,13 @@ static bool qemu_co_queue_do_restart(CoQueue *queue, bool single)
|
||||
Coroutine *self = qemu_coroutine_self();
|
||||
Coroutine *next;
|
||||
|
||||
if (QTAILQ_EMPTY(&queue->entries)) {
|
||||
if (QSIMPLEQ_EMPTY(&queue->entries)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while ((next = QTAILQ_FIRST(&queue->entries)) != NULL) {
|
||||
QTAILQ_REMOVE(&queue->entries, next, co_queue_next);
|
||||
QTAILQ_INSERT_TAIL(&self->co_queue_wakeup, next, co_queue_next);
|
||||
while ((next = QSIMPLEQ_FIRST(&queue->entries)) != NULL) {
|
||||
QSIMPLEQ_REMOVE_HEAD(&queue->entries, co_queue_next);
|
||||
QSIMPLEQ_INSERT_TAIL(&self->co_queue_wakeup, next, co_queue_next);
|
||||
trace_qemu_co_queue_next(next);
|
||||
if (single) {
|
||||
break;
|
||||
@ -97,19 +97,19 @@ bool qemu_co_enter_next(CoQueue *queue)
|
||||
{
|
||||
Coroutine *next;
|
||||
|
||||
next = QTAILQ_FIRST(&queue->entries);
|
||||
next = QSIMPLEQ_FIRST(&queue->entries);
|
||||
if (!next) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QTAILQ_REMOVE(&queue->entries, next, co_queue_next);
|
||||
qemu_coroutine_enter(next, NULL);
|
||||
QSIMPLEQ_REMOVE_HEAD(&queue->entries, co_queue_next);
|
||||
qemu_coroutine_enter(next);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool qemu_co_queue_empty(CoQueue *queue)
|
||||
{
|
||||
return QTAILQ_FIRST(&queue->entries) == NULL;
|
||||
return QSIMPLEQ_FIRST(&queue->entries) == NULL;
|
||||
}
|
||||
|
||||
void qemu_co_mutex_init(CoMutex *mutex)
|
||||
|
@ -25,7 +25,7 @@ static void co_sleep_cb(void *opaque)
|
||||
{
|
||||
CoSleepCB *sleep_cb = opaque;
|
||||
|
||||
qemu_coroutine_enter(sleep_cb->co, NULL);
|
||||
qemu_coroutine_enter(sleep_cb->co);
|
||||
}
|
||||
|
||||
void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type,
|
||||
|
@ -42,7 +42,7 @@ static void coroutine_pool_cleanup(Notifier *n, void *value)
|
||||
}
|
||||
}
|
||||
|
||||
Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
|
||||
Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque)
|
||||
{
|
||||
Coroutine *co = NULL;
|
||||
|
||||
@ -76,7 +76,8 @@ Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
|
||||
}
|
||||
|
||||
co->entry = entry;
|
||||
QTAILQ_INIT(&co->co_queue_wakeup);
|
||||
co->entry_arg = opaque;
|
||||
QSIMPLEQ_INIT(&co->co_queue_wakeup);
|
||||
return co;
|
||||
}
|
||||
|
||||
@ -100,12 +101,12 @@ static void coroutine_delete(Coroutine *co)
|
||||
qemu_coroutine_delete(co);
|
||||
}
|
||||
|
||||
void qemu_coroutine_enter(Coroutine *co, void *opaque)
|
||||
void qemu_coroutine_enter(Coroutine *co)
|
||||
{
|
||||
Coroutine *self = qemu_coroutine_self();
|
||||
CoroutineAction ret;
|
||||
|
||||
trace_qemu_coroutine_enter(self, co, opaque);
|
||||
trace_qemu_coroutine_enter(self, co, co->entry_arg);
|
||||
|
||||
if (co->caller) {
|
||||
fprintf(stderr, "Co-routine re-entered recursively\n");
|
||||
@ -113,7 +114,6 @@ void qemu_coroutine_enter(Coroutine *co, void *opaque)
|
||||
}
|
||||
|
||||
co->caller = self;
|
||||
co->entry_arg = opaque;
|
||||
ret = qemu_coroutine_switch(self, co, COROUTINE_ENTER);
|
||||
|
||||
qemu_co_queue_run_restart(co);
|
||||
|
Loading…
Reference in New Issue
Block a user