static int perform_cow(BlockDriverState *bs, QCowL2Meta *m) { BDRVQcow2State *s = bs->opaque; Qcow2COWRegion *start = &m->cow_start; Qcow2COWRegion *end = &m->cow_end; unsigned buffer_size; unsigned data_bytes = end->offset - (start->offset + start->nb_bytes); bool merge_reads; uint8_t *start_buffer, *end_buffer; QEMUIOVector qiov; int ret; assert(start->nb_bytes <= UINT_MAX - end->nb_bytes); assert(start->nb_bytes + end->nb_bytes <= UINT_MAX - data_bytes); assert(start->offset + start->nb_bytes <= end->offset); assert(!m->data_qiov || m->data_qiov->size == data_bytes); if (start->nb_bytes == 0 && end->nb_bytes == 0) { return 0; } /* If we have to read both the start and end COW regions and the * middle region is not too large then perform just one read * operation */ merge_reads = start->nb_bytes && end->nb_bytes && data_bytes <= 16384; if (merge_reads) { buffer_size = start->nb_bytes + data_bytes + end->nb_bytes; } else { /* If we have to do two reads, add some padding in the middle * if necessary to make sure that the end region is optimally * aligned. */ size_t align = bdrv_opt_mem_align(bs); assert(align > 0 && align <= UINT_MAX); assert(QEMU_ALIGN_UP(start->nb_bytes, align) <= UINT_MAX - end->nb_bytes); buffer_size = QEMU_ALIGN_UP(start->nb_bytes, align) + end->nb_bytes; } /* Reserve a buffer large enough to store all the data that we're * going to read */ start_buffer = qemu_try_blockalign(bs, buffer_size); if (start_buffer == NULL) { return -ENOMEM; } /* The part of the buffer where the end region is located */ end_buffer = start_buffer + buffer_size - end->nb_bytes; qemu_iovec_init(&qiov, 2 + (m->data_qiov ? m->data_qiov->niov : 0)); qemu_co_mutex_unlock(&s->lock); /* First we read the existing data from both COW regions. We * either read the whole region in one go, or the start and end * regions separately. */ if (merge_reads) { qemu_iovec_add(&qiov, start_buffer, buffer_size); ret = do_perform_cow_read(bs, m->offset, start->offset, &qiov); } else { qemu_iovec_add(&qiov, start_buffer, start->nb_bytes); ret = do_perform_cow_read(bs, m->offset, start->offset, &qiov); if (ret < 0) { goto fail; } qemu_iovec_reset(&qiov); qemu_iovec_add(&qiov, end_buffer, end->nb_bytes); ret = do_perform_cow_read(bs, m->offset, end->offset, &qiov); } if (ret < 0) { goto fail; } /* Encrypt the data if necessary before writing it */ if (bs->encrypted) { if (!do_perform_cow_encrypt(bs, m->offset, start->offset, start_buffer, start->nb_bytes) || !do_perform_cow_encrypt(bs, m->offset, end->offset, end_buffer, end->nb_bytes)) { ret = -EIO; goto fail; } } /* And now we can write everything. If we have the guest data we * can write everything in one single operation */ if (m->data_qiov) { qemu_iovec_reset(&qiov); if (start->nb_bytes) { qemu_iovec_add(&qiov, start_buffer, start->nb_bytes); } qemu_iovec_concat(&qiov, m->data_qiov, 0, data_bytes); if (end->nb_bytes) { qemu_iovec_add(&qiov, end_buffer, end->nb_bytes); } /* NOTE: we have a write_aio blkdebug event here followed by * a cow_write one in do_perform_cow_write(), but there's only * one single I/O operation */ BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); ret = do_perform_cow_write(bs, m->alloc_offset, start->offset, &qiov); } else { /* If there's no guest data then write both COW regions separately */ qemu_iovec_reset(&qiov); qemu_iovec_add(&qiov, start_buffer, start->nb_bytes); ret = do_perform_cow_write(bs, m->alloc_offset, start->offset, &qiov); if (ret < 0) { goto fail; } qemu_iovec_reset(&qiov); qemu_iovec_add(&qiov, end_buffer, end->nb_bytes); ret = do_perform_cow_write(bs, m->alloc_offset, end->offset, &qiov); } fail: qemu_co_mutex_lock(&s->lock); /* * Before we update the L2 table to actually point to the new cluster, we * need to be sure that the refcounts have been increased and COW was * handled. */ if (ret == 0) { qcow2_cache_depends_on_flush(s->l2_table_cache); } qemu_vfree(start_buffer); qemu_iovec_destroy(&qiov); return ret; }