Commit b11a24de authored by Kevin Wolf's avatar Kevin Wolf
Browse files

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: default avatarKevin Wolf <kwolf@redhat.com>
parent 42496d62
Loading
Loading
Loading
Loading
+56 −2
Original line number Diff line number Diff line
@@ -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;
}