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:
Peter Maydell 2016-07-14 11:48:46 +01:00
commit 9358450e98
69 changed files with 913 additions and 406 deletions

View File

@ -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);
}

View File

@ -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:

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}
/*

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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) {

View File

@ -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,

View File

@ -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

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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);
}
/*

View File

@ -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)

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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
View File

@ -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);

View File

@ -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. */

View File

@ -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;
}

View File

@ -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)

View File

@ -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;

View File

@ -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),

View File

@ -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,

View File

@ -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));

View File

@ -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),\

View File

@ -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), \

View File

@ -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;

View File

@ -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,

View File

@ -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:

View File

@ -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 */

View File

@ -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) \

View File

@ -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"

View File

@ -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;
/**

View File

@ -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);

View File

@ -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 (...) {

View File

@ -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"

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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
#

View File

@ -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:

View File

@ -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;
}

View File

@ -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

View File

@ -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"])

View File

@ -1,5 +1,5 @@
....
.....
----------------------------------------------------------------------
Ran 4 tests
Ran 5 tests
OK

89
tests/qemu-iotests/157 Executable file
View 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

View 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

View File

@ -156,3 +156,4 @@
154 rw auto backing quick
155 rw auto
156 rw auto quick
157 auto

View File

@ -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;
}

View File

@ -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));

View File

@ -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. */

View File

@ -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,

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -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,

View File

@ -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);