Esempio n. 1
0
static void virtio_blk_handle_request(VirtIOBlockReq *req,
    MultiReqBuffer *mrb)
{
    if (req->elem.out_num < 1 || req->elem.in_num < 1) {
        fprintf(stderr, "virtio-blk missing headers\n");
        exit(1);
    }

    if (req->elem.out_sg[0].iov_len < sizeof(*req->out) ||
        req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) {
        fprintf(stderr, "virtio-blk header not in correct element\n");
        exit(1);
    }

    req->out = (void *)req->elem.out_sg[0].iov_base;
    req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;

    if (req->out->type & VIRTIO_BLK_T_FLUSH) {
        virtio_blk_handle_flush(req, mrb);
    } else if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) {
        virtio_blk_handle_scsi(req);
    } else if (req->out->type & VIRTIO_BLK_T_OUT) {
        qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
                                 req->elem.out_num - 1);
        virtio_blk_handle_write(req, mrb);
    } else {
        qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0],
                                 req->elem.in_num - 1);
        virtio_blk_handle_read(req);
    }
}
Esempio n. 2
0
static void virtio_blk_handle_request(VirtIOBlockReq *req,
    MultiReqBuffer *mrb)
{
    uint32_t type;

    if (req->elem.out_num < 1 || req->elem.in_num < 1) {
        error_report("virtio-blk missing headers");
        exit(1);
    }

    if (req->elem.out_sg[0].iov_len < sizeof(*req->out) ||
        req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) {
        error_report("virtio-blk header not in correct element");
        exit(1);
    }

    req->out = (void *)req->elem.out_sg[0].iov_base;
    req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;

    type = ldl_p(&req->out->type);

    if (type & VIRTIO_BLK_T_FLUSH) {
        virtio_blk_handle_flush(req, mrb);
    } else if (type & VIRTIO_BLK_T_SCSI_CMD) {
        virtio_blk_handle_scsi(req);
    } else if (type & VIRTIO_BLK_T_GET_ID) {
        VirtIOBlock *s = req->dev;

        /*
         * NB: per existing s/n string convention the string is
         * terminated by '\0' only when shorter than buffer.
         */
        strncpy(req->elem.in_sg[0].iov_base,
                s->blk.serial ? s->blk.serial : "",
                MIN(req->elem.in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES));
        virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
        g_free(req);
    } else if (type & VIRTIO_BLK_T_OUT) {
        qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
                                 req->elem.out_num - 1);
        virtio_blk_handle_write(req, mrb);
    } else if (type == VIRTIO_BLK_T_IN || type == VIRTIO_BLK_T_BARRIER) {
        /* VIRTIO_BLK_T_IN is 0, so we can't just & it. */
        qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0],
                                 req->elem.in_num - 1);
        virtio_blk_handle_read(req);
    } else {
        virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
        g_free(req);
    }
}
Esempio n. 3
0
/**
 * Update header in-place (does not rewrite backing filename or other strings)
 *
 * This function only updates known header fields in-place and does not affect
 * extra data after the QED header.
 */
static void qed_write_header(BDRVQEDState *s, BlockDriverCompletionFunc cb,
                             void *opaque)
{
    /* We must write full sectors for O_DIRECT but cannot necessarily generate
     * the data following the header if an unrecognized compat feature is
     * active.  Therefore, first read the sectors containing the header, update
     * them, and write back.
     */

    int nsectors = (sizeof(QEDHeader) + BDRV_SECTOR_SIZE - 1) /
                   BDRV_SECTOR_SIZE;
    size_t len = nsectors * BDRV_SECTOR_SIZE;
    QEDWriteHeaderCB *write_header_cb = gencb_alloc(sizeof(*write_header_cb),
                                                    cb, opaque);

    write_header_cb->s = s;
    write_header_cb->nsectors = nsectors;
    write_header_cb->buf = qemu_blockalign(s->bs, len);
    write_header_cb->iov.iov_base = write_header_cb->buf;
    write_header_cb->iov.iov_len = len;
    qemu_iovec_init_external(&write_header_cb->qiov, &write_header_cb->iov, 1);

    bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors,
                   qed_write_header_read_cb, write_header_cb);
}
Esempio n. 4
0
static void virtio_blk_handle_request(VirtIOBlockReq *req,
    MultiReqBuffer *mrb)
{
    if (req->elem.out_num < 1 || req->elem.in_num < 1) {
        fprintf(stderr, "virtio-blk missing headers\n");
        exit(1);
    }

    if (req->elem.out_sg[0].iov_len < sizeof(*req->out) ||
        req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) {
        fprintf(stderr, "virtio-blk header not in correct element\n");
        exit(1);
    }

    req->out = (void *)req->elem.out_sg[0].iov_base;
    req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;

    if (req->out->type & VIRTIO_BLK_T_FLUSH) {
        virtio_blk_handle_flush(mrb->blkreq, &mrb->num_writes,
            req, &mrb->old_bs);
    } else if (req->out->type & VIRTIO_BLK_T_SCSI_CMD) {
        virtio_blk_handle_scsi(req);
    } else if (req->out->type & VIRTIO_BLK_T_GET_ID) {
        VirtIOBlock *s = req->dev;

        /*
         * NB: per existing s/n string convention the string is
         * terminated by '\0' only when shorter than buffer.
         */
        strncpy(req->elem.in_sg[0].iov_base,
                s->blk->serial ? s->blk->serial : "",
                MIN(req->elem.in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES));
        virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
        qemu_free(req);
    } else if (req->out->type & VIRTIO_BLK_T_OUT) {
        qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
                                 req->elem.out_num - 1);
        virtio_blk_handle_write(mrb->blkreq, &mrb->num_writes,
            req, &mrb->old_bs);
    } else {
        qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0],
                                 req->elem.in_num - 1);
        virtio_blk_handle_read(req);
    }
}
Esempio n. 5
0
static void virtio_blk_handle_request(VirtIOBlockReq *req,
    MultiReqBuffer *mrb)
{
    uint32_t type;

    if (req->elem.out_num < 1 || req->elem.in_num < 1) {
        error_report("virtio-blk missing headers");
        exit(1);
    }

    if (req->elem.out_sg[0].iov_len < sizeof(*req->out) ||
        req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) {
        error_report("virtio-blk header not in correct element");
        exit(1);
    }

    req->out = (void *)req->elem.out_sg[0].iov_base;
    req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base;

    type = ldl_p(&req->out->type);

    if (type & VIRTIO_BLK_T_FLUSH) {
        virtio_blk_handle_flush(req, mrb);
    } else if (type & VIRTIO_BLK_T_SCSI_CMD) {
        virtio_blk_handle_scsi(req);
    } else if (type & VIRTIO_BLK_T_GET_ID) {
        VirtIOBlock *s = req->dev;

        memcpy(req->elem.in_sg[0].iov_base, s->sn,
               MIN(req->elem.in_sg[0].iov_len, sizeof(s->sn)));
        virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
    } else if (type & VIRTIO_BLK_T_OUT) {
        qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1],
                                 req->elem.out_num - 1);
        virtio_blk_handle_write(req, mrb);
    } else {
        qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0],
                                 req->elem.in_num - 1);
        virtio_blk_handle_read(req);
    }
}
Esempio n. 6
0
static ssize_t block_writev_buffer(void *opaque, struct iovec *iov, int iovcnt,
                                   int64_t pos)
{
    int ret;
    QEMUIOVector qiov;

    qemu_iovec_init_external(&qiov, iov, iovcnt);
    ret = bdrv_writev_vmstate(opaque, &qiov, pos);
    if (ret < 0) {
        return ret;
    }

    return qiov.size;
}
Esempio n. 7
0
/**
 * Write out an updated part or all of a table
 *
 * @s:          QED state
 * @offset:     Offset of table in image file, in bytes
 * @table:      Table
 * @index:      Index of first element
 * @n:          Number of elements
 * @flush:      Whether or not to sync to disk
 * @cb:         Completion function
 * @opaque:     Argument for completion function
 */
static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
                            unsigned int index, unsigned int n, bool flush,
                            BlockDriverCompletionFunc *cb, void *opaque)
{
    QEDWriteTableCB *write_table_cb;
    BlockDriverAIOCB *aiocb;
    unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1;
    unsigned int start, end, i;
    size_t len_bytes;

    trace_qed_write_table(s, offset, table, index, n);

    /* Calculate indices of the first and one after last elements */
    start = index & ~sector_mask;
    end = (index + n + sector_mask) & ~sector_mask;

    len_bytes = (end - start) * sizeof(uint64_t);

    write_table_cb = gencb_alloc(sizeof(*write_table_cb), cb, opaque);
    write_table_cb->s = s;
    write_table_cb->orig_table = table;
    write_table_cb->flush = flush;
    write_table_cb->table = qemu_blockalign(s->bs, len_bytes);
    write_table_cb->iov.iov_base = write_table_cb->table->offsets;
    write_table_cb->iov.iov_len = len_bytes;
    qemu_iovec_init_external(&write_table_cb->qiov, &write_table_cb->iov, 1);

    /* Byteswap table */
    for (i = start; i < end; i++) {
        uint64_t le_offset = cpu_to_le64(table->offsets[i]);
        write_table_cb->table->offsets[i - start] = le_offset;
    }

    /* Adjust for offset into table */
    offset += start * sizeof(uint64_t);

    aiocb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE,
                            &write_table_cb->qiov,
                            write_table_cb->qiov.size / BDRV_SECTOR_SIZE,
                            qed_write_table_cb, write_table_cb);
    if (!aiocb) {
        qed_write_table_cb(write_table_cb, -EIO);
    }
}
Esempio n. 8
0
static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table,
                           BlockDriverCompletionFunc *cb, void *opaque)
{
    QEDReadTableCB *read_table_cb = gencb_alloc(sizeof(*read_table_cb),
                                                cb, opaque);
    QEMUIOVector *qiov = &read_table_cb->qiov;

    trace_qed_read_table(s, offset, table);

    read_table_cb->s = s;
    read_table_cb->table = table;
    read_table_cb->iov.iov_base = table->offsets,
    read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size,

    qemu_iovec_init_external(qiov, &read_table_cb->iov, 1);
    bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov,
                   qiov->size / BDRV_SECTOR_SIZE,
                   qed_read_table_cb, read_table_cb);
}
Esempio n. 9
0
static int compare_full_images (void)
{
    CompareFullCB *cf;
    int old_copy_on_read = FALSE;

    printf ("Performing a full comparison of the truth image and "
            "the test image...\n");

    if (!strncmp (bs->drv->format_name, "fvd", 3)) {
        /* Disable copy-on-read when scanning through the entire image. */
        old_copy_on_read = fvd_get_copy_on_read (bs);
        fvd_set_copy_on_read (bs, FALSE);
    }

    cf = g_malloc(sizeof(CompareFullCB));
    cf->max_nb_sectors = 1048576L / 512;
    cf->nb_sectors = MIN (cf->max_nb_sectors, total_sectors);
    if (posix_memalign ((void **) &cf->truth_buf, 512,
                        cf->max_nb_sectors * 512) != 0) {
        die ("posix_memalign");
    }
    cf->iov.iov_base = qemu_blockalign (bs, cf->max_nb_sectors * 512);
    cf->iov.iov_len = cf->nb_sectors * 512;
    cf->sector_num = 0;
    qemu_iovec_init_external (&cf->qiov, &cf->iov, 1);
    if (!bdrv_aio_readv (bs, cf->sector_num, &cf->qiov,
                         cf->nb_sectors, compare_full_images_cb, cf)) {
        die ("bdrv_aio_readv\n");
    }

    sim_all_tasks ();

    if (!strncmp (bs->drv->format_name, "fvd", 3)) {
        fvd_set_copy_on_read (bs, old_copy_on_read);
    }

    return 0;
}
Esempio n. 10
0
static void compare_full_images_cb (void *opaque, int ret)
{
    CompareFullCB *cf = opaque;

    if (ret) {
        /* Failed. Retry the operation. */
        bdrv_aio_readv (bs, cf->sector_num, &cf->qiov, cf->nb_sectors,
                        compare_full_images_cb, cf);
        return;
    }

    truth_io (cf->truth_buf, cf->sector_num, cf->nb_sectors, TRUE);
    verify (cf->truth_buf, cf->iov.iov_base, cf->sector_num, cf->nb_sectors);

    cf->sector_num += cf->nb_sectors;
    if (cf->sector_num >= total_sectors) {
        /* Finished. */
        free (cf->truth_buf);
        qemu_vfree (cf->iov.iov_base);
        g_free(cf);
        return;
    }

    /* Read more data to compare. */
    if (cf->sector_num + cf->max_nb_sectors > total_sectors) {
        cf->nb_sectors = total_sectors - cf->sector_num;
    } else {
        cf->nb_sectors = cf->max_nb_sectors;
    }
    cf->iov.iov_len = cf->nb_sectors * 512;
    qemu_iovec_init_external (&cf->qiov, &cf->iov, 1);
    if (!bdrv_aio_readv (bs, cf->sector_num, &cf->qiov,
                         cf->nb_sectors, compare_full_images_cb, cf)) {
        die ("bdrv_aio_readv\n");
    }
}
Esempio n. 11
0
void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb)
{
    uint32_t type;
    struct iovec *in_iov = req->elem->in_sg;
    struct iovec *iov = req->elem->out_sg;
    unsigned in_num = req->elem->in_num;
    unsigned out_num = req->elem->out_num;

    if (req->elem->out_num < 1 || req->elem->in_num < 1) {
        error_report("virtio-blk missing headers");
        exit(1);
    }

    if (unlikely(iov_to_buf(iov, out_num, 0, &req->out,
                            sizeof(req->out)) != sizeof(req->out))) {
        error_report("virtio-blk request outhdr too short");
        exit(1);
    }

    iov_discard_front(&iov, &out_num, sizeof(req->out));

    if (in_num < 1 ||
        in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) {
        error_report("virtio-blk request inhdr too short");
        exit(1);
    }

    req->in = (void *)in_iov[in_num - 1].iov_base
              + in_iov[in_num - 1].iov_len
              - sizeof(struct virtio_blk_inhdr);
    iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr));

    type = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type);

    if (type & VIRTIO_BLK_T_FLUSH) {
        virtio_blk_handle_flush(req, mrb);
    } else if (type & VIRTIO_BLK_T_SCSI_CMD) {
        virtio_blk_handle_scsi(req);
    } else if (type & VIRTIO_BLK_T_GET_ID) {
        VirtIOBlock *s = req->dev;

        /*
         * NB: per existing s/n string convention the string is
         * terminated by '\0' only when shorter than buffer.
         */
        strncpy(req->elem->in_sg[0].iov_base,
                s->blk.serial ? s->blk.serial : "",
                MIN(req->elem->in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES));
        virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
        virtio_blk_free_request(req);
    } else if (type & VIRTIO_BLK_T_OUT) {
        qemu_iovec_init_external(&req->qiov, &req->elem->out_sg[1],
                                 req->elem->out_num - 1);
        virtio_blk_handle_write(req, mrb);
    } else if (type == VIRTIO_BLK_T_IN || type == VIRTIO_BLK_T_BARRIER) {
        /* VIRTIO_BLK_T_IN is 0, so we can't just & it. */
        qemu_iovec_init_external(&req->qiov, &req->elem->in_sg[0],
                                 req->elem->in_num - 1);
        virtio_blk_handle_read(req);
    } else {
        virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP);
        virtio_blk_free_request(req);
    }
}
Esempio n. 12
0
static void prepare_read_write (RandomIO * r)
{
    /* Do a READ or WRITE? */
    if (random () % 2) {
        r->type = OP_READ;
    } else {
        r->type = OP_WRITE;
    }

    /* Find the next region to perform io. */
    do {
        if (parallel <= 1 || (random () % 2 == 0)) {
            /* Perform a random I/O. */
            r->sector_num = rand64 () % total_sectors;
        } else {
            /* Perform an I/O next to a currently ongoing I/O. */
            int id;
            do {
                id = random () % parallel;
            } while (id == r->tester);

            RandomIO *p = &testers[id];
            r->sector_num =
                p->sector_num + 2 * io_size - rand64 () % (4 * io_size);
            if (r->sector_num < 0) {
                r->sector_num = 0;
            } else if (r->sector_num >= total_sectors) {
                r->sector_num = total_sectors - 1;
            }
        }

        r->nb_sectors = 1 + rand64 () % io_size;
        if (r->sector_num + r->nb_sectors > total_sectors) {
            r->nb_sectors = total_sectors - r->sector_num;
        }
    } while (check_conflict (r));

    if (r->type == OP_WRITE) {
        /* Fill test_buf with random data. */
        int i, j;
        for (i = 0; i < r->nb_sectors; i++) {
            const uint64_t TEST_MAGIC = 0x0123456789ABCDEFULL;
            /* This first 8 bytes of the sector stores the current testing
             * round. The next 8 bytes store a magic number.  This info helps
             * debugging. */
            uint64_t *p = (uint64_t *) & r->test_buf[i * 512];
            *p = r->uuid;
            cpu_to_be64s (p);
            p++;
            *p = TEST_MAGIC;
            cpu_to_be64s (p);

            /* The rest of the sector are filled with random data. */
            uint32_t *q = (uint32_t *) (p + 1);
            int n = (512 - 2 * sizeof (uint64_t)) / sizeof (uint32_t);
            for (j = 0; j < n; j++) {
                *q++ = random ();
            }
        }
    }

    /* Determine the number of iov. */
    int niov = 0;
    uint8_t *p = r->test_buf;
    int left = r->nb_sectors;
    do {
        if (niov == max_iov - 1) {
            r->qiov.iov[niov].iov_len = left * 512;
            r->qiov.iov[niov].iov_base = p;
            niov++;
            break;
        }

        int nb = 1 + random () % left;
        r->qiov.iov[niov].iov_len = nb * 512;
        r->qiov.iov[niov].iov_base = p;
        p += r->qiov.iov[niov].iov_len;
        left -= nb;
        niov++;
    } while (left > 0);

    qemu_iovec_init_external (&r->qiov, r->qiov.iov, niov);
}
Esempio n. 13
0
/* Store data in the compact image. The argument 'soft_write' means
 * the store was caused by copy-on-read or prefetching, which need not
 * update metadata immediately. */
static BlockDriverAIOCB *store_data_in_compact_image (FvdAIOCB * acb,
                                                      int soft_write,
                                                      FvdAIOCB * parent_acb,
                                                      BlockDriverState * bs,
                                                      int64_t sector_num,
                                                      QEMUIOVector * orig_qiov,
                                                      const int nb_sectors,
                                                      BlockDriverCompletionFunc
                                                      * cb, void *opaque)
{
    BDRVFvdState *s = bs->opaque;

    const uint32_t first_chunk = sector_num / s->chunk_size;
    const uint32_t last_chunk = (sector_num + nb_sectors - 1) / s->chunk_size;
    int table_dirty = FALSE;
    uint32_t chunk;
    int64_t start_sec;

    /* Check if storag space is allocated. */
    for (chunk = first_chunk; chunk <= last_chunk; chunk++) {
        if (IS_EMPTY (s->table[chunk])) {
            uint32_t id = allocate_chunk (bs);
            if (IS_EMPTY (id)) {
                return NULL;
            }
            id |= DIRTY_TABLE;
            WRITE_TABLE (s->table[chunk], id);

            table_dirty = TRUE;
        } else if (IS_DIRTY (s->table[chunk])) {
            /* This is possible if a previous soft-write allocated the storage
             * space but did not flush the table entry change to the journal
             * and hence did not clean the dirty bit. This is also possible
             * with two concurrent hard-writes. The first hard-write allocated
             * the storage space but has not flushed the table entry change to
             * the journal yet and hence the table entry remains dirty. In
             * this case, the second hard-write will also try to flush this
             * dirty table entry to the journal. The outcome is correct since
             * they store the same metadata change in the journal (although
             * twice). For this race condition, we prefer to have two writes
             * to the journal rather than introducing a locking mechanism,
             * because this happens rarely and those two writes to the journal
             * are likely to be merged by the kernel into a single write since
             * they are likely to update back-to-back sectors in the journal.
             * A locking mechanism would be less efficient, because the large
             * size of chunks would cause unnecessary locking due to ``false
             * sharing'' of a chunk by two writes. */
            table_dirty = TRUE;
        }
    }

    const int update_table = (!soft_write && table_dirty);
    size_t iov_left;
    uint8_t *iov_buf;
    int nb, iov_index, nqiov, niov;
    uint32_t prev;

    if (first_chunk == last_chunk) {
        goto handle_one_continuous_region;
    }

    /* Count the number of qiov and iov needed to cover the continuous regions
     * of the compact image. */
    iov_left = orig_qiov->iov[0].iov_len;
    iov_buf = orig_qiov->iov[0].iov_base;
    iov_index = 0;
    nqiov = 0;
    niov = 0;
    prev = READ_TABLE (s->table[first_chunk]);

    /* Data in the first chunk. */
    nb = s->chunk_size - (sector_num % s->chunk_size);

    for (chunk = first_chunk + 1; chunk <= last_chunk; chunk++) {
        uint32_t current = READ_TABLE (s->table[chunk]);
        int64_t data_size;
        if (chunk < last_chunk) {
            data_size = s->chunk_size;
        } else {
            data_size = (sector_num + nb_sectors) % s->chunk_size;
            if (data_size == 0) {
                data_size = s->chunk_size;
            }
        }

        if (current == prev + 1) {
            nb += data_size;        /* Continue the previous region. */
        } else {
            /* Terminate the previous region. */
            niov +=
                count_iov (orig_qiov->iov, &iov_index, &iov_buf, &iov_left,
                           nb * 512);
            nqiov++;
            nb = data_size;        /* Data in the new region. */
        }
        prev = current;
    }

    if (nqiov == 0) {
      handle_one_continuous_region:
        /* A simple case. All data can be written out in one qiov and no new
         * chunks are allocated. */
        start_sec = READ_TABLE (s->table[first_chunk]) * s->chunk_size +
                                        (sector_num % s->chunk_size);

        if (!update_table && !acb) {
            if (parent_acb) {
                QDEBUG ("STORE: acb%llu-%p  "
                        "store_directly_without_table_update\n",
                        parent_acb->uuid, parent_acb);
            }
            return bdrv_aio_writev (s->fvd_data, s->data_offset + start_sec,
                                    orig_qiov, nb_sectors, cb, opaque);
        }

        if (!acb && !(acb = init_store_acb (soft_write, orig_qiov, bs,
                            sector_num, nb_sectors, parent_acb, cb, opaque))) {
            return NULL;
        }

        QDEBUG ("STORE: acb%llu-%p  store_directly  sector_num=%" PRId64
                " nb_sectors=%d\n", acb->uuid, acb, acb->sector_num,
                acb->nb_sectors);

        acb->store.update_table = update_table;
        acb->store.num_children = 1;
        acb->store.one_child.hd_acb =
            bdrv_aio_writev (s->fvd_data, s->data_offset + start_sec, orig_qiov,
                             nb_sectors, finish_store_data_in_compact_image,
                             &acb->store.one_child);
        if (acb->store.one_child.hd_acb) {
            acb->store.one_child.acb = acb;
            return &acb->common;
        } else {
            my_qemu_aio_unref (acb);
            return NULL;
        }
    }

    /* qiov for the last continuous region. */
    niov += count_iov (orig_qiov->iov, &iov_index, &iov_buf,
                       &iov_left, nb * 512);
    nqiov++;
    ASSERT (iov_index == orig_qiov->niov - 1 && iov_left == 0);

    /* Need to submit multiple requests to the lower layer. */
    if (!acb && !(acb = init_store_acb (soft_write, orig_qiov, bs, sector_num,
                                        nb_sectors, parent_acb, cb, opaque))) {
        return NULL;
    }
    acb->store.update_table = update_table;
    acb->store.num_children = nqiov;

    if (!parent_acb) {
        QDEBUG ("STORE: acb%llu-%p  start  sector_num=%" PRId64
                " nb_sectors=%d\n", acb->uuid, acb, acb->sector_num,
                acb->nb_sectors);
    }

    /* Allocate memory and create multiple requests. */
    const size_t metadata_size = nqiov * (sizeof (CompactChildCB) +
                                          sizeof (QEMUIOVector))
                                    + niov * sizeof (struct iovec);
    acb->store.children = (CompactChildCB *) my_qemu_malloc (metadata_size);
    QEMUIOVector *q = (QEMUIOVector *) (acb->store.children + nqiov);
    struct iovec *v = (struct iovec *) (q + nqiov);

    start_sec = READ_TABLE (s->table[first_chunk]) * s->chunk_size +
                                        (sector_num % s->chunk_size);
    nqiov = 0;
    iov_index = 0;
    iov_left = orig_qiov->iov[0].iov_len;
    iov_buf = orig_qiov->iov[0].iov_base;
    prev = READ_TABLE (s->table[first_chunk]);

    /* Data in the first chunk. */
    if (first_chunk == last_chunk) {
        nb = nb_sectors;
    }
    else {
        nb = s->chunk_size - (sector_num % s->chunk_size);
    }

    for (chunk = first_chunk + 1; chunk <= last_chunk; chunk++) {
        uint32_t current = READ_TABLE (s->table[chunk]);
        int64_t data_size;
        if (chunk < last_chunk) {
            data_size = s->chunk_size;
        } else {
            data_size = (sector_num + nb_sectors) % s->chunk_size;
            if (data_size == 0) {
                data_size = s->chunk_size;
            }
        }

        if (current == prev + 1) {
            nb += data_size;        /* Continue the previous region. */
        } else {
            /* Terminate the previous continuous region. */
            niov = setup_iov (orig_qiov->iov, v, &iov_index,
                              &iov_buf, &iov_left, nb * 512);
            qemu_iovec_init_external (q, v, niov);
            QDEBUG ("STORE: acb%llu-%p  create_child %d sector_num=%" PRId64
                    " nb_sectors=%d niov=%d\n", acb->uuid, acb, nqiov,
                    start_sec, q->size / 512, q->niov);
            acb->store.children[nqiov].hd_acb =
                bdrv_aio_writev (s->fvd_data, s->data_offset + start_sec, q,
                                 q->size / 512,
                                 finish_store_data_in_compact_image,
                                 &acb->store.children[nqiov]);
            if (!acb->store.children[nqiov].hd_acb) {
                goto fail;
            }
            acb->store.children[nqiov].acb = acb;
            v += niov;
            q++;
            nqiov++;
            start_sec = current * s->chunk_size; /* Begin of the new region. */
            nb = data_size;        /* Data in the new region. */
        }
        prev = current;
    }

    /* Requst for the last chunk. */
    niov = setup_iov (orig_qiov->iov, v, &iov_index, &iov_buf,
                      &iov_left, nb * 512);
    ASSERT (iov_index == orig_qiov->niov - 1 && iov_left == 0);
    qemu_iovec_init_external (q, v, niov);

    QDEBUG ("STORE: acb%llu-%p  create_child_last %d sector_num=%" PRId64
            " nb_sectors=%d niov=%d\n", acb->uuid, acb, nqiov, start_sec,
            q->size / 512, q->niov);
    acb->store.children[nqiov].hd_acb =
        bdrv_aio_writev (s->fvd_data, s->data_offset + start_sec, q,
                         q->size / 512, finish_store_data_in_compact_image,
                         &acb->store.children[nqiov]);
    if (acb->store.children[nqiov].hd_acb) {
        acb->store.children[nqiov].acb = acb;
        return &acb->common;
    }

    int i;
  fail:
    QDEBUG ("STORE: acb%llu-%p  failed\n", acb->uuid, acb);
    for (i = 0; i < nqiov; i++) {
        bdrv_aio_cancel (acb->store.children[i].hd_acb);
    }
    my_qemu_free (acb->store.children);
    my_qemu_aio_unref (acb);
    return NULL;
}