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