diff options
author | Kevin Wolf <kwolf@redhat.com> | 2011-06-07 15:20:44 +0200 |
---|---|---|
committer | Kevin Wolf <kwolf@redhat.com> | 2011-06-14 17:03:27 +0200 |
commit | b11a24dee661dd1e1de0dcbc149052ed67b0647a (patch) | |
tree | 9d699c0f991f57b28267c744a7b90f775e8f4c9c /block/qcow.c | |
parent | 42496d6240bfedc7ac6d92f04f92cff6c2e9f226 (diff) |
qcow: Avoid direct AIO callback
bdrv_aio_* must not call the callback before returning to its caller. In qcow,
this could happen in some error cases. This starts the real requests processing
in a BH to avoid this situation.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Diffstat (limited to 'block/qcow.c')
-rw-r--r-- | block/qcow.c | 58 |
1 files changed, 56 insertions, 2 deletions
diff --git a/block/qcow.c b/block/qcow.c index a26c88620f..227b104e36 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -496,6 +496,8 @@ typedef struct QCowAIOCB { uint64_t cluster_offset; uint8_t *cluster_data; struct iovec hd_iov; + bool is_write; + QEMUBH *bh; QEMUIOVector hd_qiov; BlockDriverAIOCB *hd_aiocb; } QCowAIOCB; @@ -525,6 +527,8 @@ static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs, acb->hd_aiocb = NULL; acb->sector_num = sector_num; acb->qiov = qiov; + acb->is_write = is_write; + if (qiov->niov > 1) { acb->buf = acb->orig_buf = qemu_blockalign(bs, qiov->size); if (is_write) @@ -538,6 +542,38 @@ static QCowAIOCB *qcow_aio_setup(BlockDriverState *bs, return acb; } +static void qcow_aio_read_cb(void *opaque, int ret); +static void qcow_aio_write_cb(void *opaque, int ret); + +static void qcow_aio_rw_bh(void *opaque) +{ + QCowAIOCB *acb = opaque; + qemu_bh_delete(acb->bh); + acb->bh = NULL; + + if (acb->is_write) { + qcow_aio_write_cb(opaque, 0); + } else { + qcow_aio_read_cb(opaque, 0); + } +} + +static int qcow_schedule_bh(QEMUBHFunc *cb, QCowAIOCB *acb) +{ + if (acb->bh) { + return -EIO; + } + + acb->bh = qemu_bh_new(cb, acb); + if (!acb->bh) { + return -EIO; + } + + qemu_bh_schedule(acb->bh); + + return 0; +} + static void qcow_aio_read_cb(void *opaque, int ret) { QCowAIOCB *acb = opaque; @@ -640,12 +676,21 @@ static BlockDriverAIOCB *qcow_aio_readv(BlockDriverState *bs, BlockDriverCompletionFunc *cb, void *opaque) { QCowAIOCB *acb; + int ret; acb = qcow_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); if (!acb) return NULL; - qcow_aio_read_cb(acb, 0); + ret = qcow_schedule_bh(qcow_aio_rw_bh, acb); + if (ret < 0) { + if (acb->qiov->niov > 1) { + qemu_vfree(acb->orig_buf); + } + qemu_aio_release(acb); + return NULL; + } + return &acb->common; } @@ -725,6 +770,7 @@ static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs, { BDRVQcowState *s = bs->opaque; QCowAIOCB *acb; + int ret; s->cluster_cache_offset = -1; /* disable compressed cache */ @@ -733,7 +779,15 @@ static BlockDriverAIOCB *qcow_aio_writev(BlockDriverState *bs, return NULL; - qcow_aio_write_cb(acb, 0); + ret = qcow_schedule_bh(qcow_aio_rw_bh, acb); + if (ret < 0) { + if (acb->qiov->niov > 1) { + qemu_vfree(acb->orig_buf); + } + qemu_aio_release(acb); + return NULL; + } + return &acb->common; } |