Pull request
* Copy offloading for qemu-img convert (iSCSI, raw, and qcow2) If the underlying storage supports copy offloading, qemu-img convert will use it instead of performing reads and writes. This avoids data transfers and thus frees up storage bandwidth for other purposes. SCSI EXTENDED COPY and Linux copy_file_range(2) are used to implement this optimization. * Drop spurious "WARNING: I\/O thread spun for 1000 iterations" warning -----BEGIN PGP SIGNATURE----- iQEcBAABAgAGBQJbFSBoAAoJEJykq7OBq3PISpEIAIcMao4/rzinAWXzS+ncK9LO 6FtRVpgutpHaWX2ayySaz5n2CdR3cNMrpCI7sjY2Kw0lrdkqxPgl5n0SWD+VCl4W 7+JLz/uF0iUV8X+99e7WGAjZbm9LSlxgn5AQKfrrwyPf0ZfzoYQ5nBMcQ6xjEeQP 48j2WqJqN9/u8RBD07o11yn0+CE5g56/f12xVjR5ASVodzsAmcZ2OQRMQbM01isU 1mBekJQkDxJkt5l13Rql8+t+vWz8/9BEW2c/eIDKvoayMqYJpdfKv4DqLloIuHnc 3RkquA0zUuKtl7xEnEkH/We7fi4QPGW/vyBN7ychS/zKzZFQrXmwqrAuFSw3dKU= =vZp+ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging Pull request * Copy offloading for qemu-img convert (iSCSI, raw, and qcow2) If the underlying storage supports copy offloading, qemu-img convert will use it instead of performing reads and writes. This avoids data transfers and thus frees up storage bandwidth for other purposes. SCSI EXTENDED COPY and Linux copy_file_range(2) are used to implement this optimization. * Drop spurious "WARNING: I\/O thread spun for 1000 iterations" warning # gpg: Signature made Mon 04 Jun 2018 12:20:08 BST # gpg: using RSA key 9CA4ABB381AB73C8 # gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>" # gpg: aka "Stefan Hajnoczi <stefanha@gmail.com>" # Primary key fingerprint: 8695 A8BF D3F9 7CDA AC35 775A 9CA4 ABB3 81AB 73C8 * remotes/stefanha/tags/block-pull-request: main-loop: drop spin_counter qemu-img: Convert with copy offloading block-backend: Add blk_co_copy_range iscsi: Implement copy offloading iscsi: Create and use iscsi_co_wait_for_task iscsi: Query and save device designator when opening file-posix: Implement bdrv_co_copy_range qcow2: Implement copy offloading raw: Implement copy offloading raw: Check byte range uniformly block: Introduce API for copy offloading Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
0d514fa234
|
@ -2211,3 +2211,21 @@ void blk_unregister_buf(BlockBackend *blk, void *host)
|
||||||
{
|
{
|
||||||
bdrv_unregister_buf(blk_bs(blk), host);
|
bdrv_unregister_buf(blk_bs(blk), host);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in,
|
||||||
|
BlockBackend *blk_out, int64_t off_out,
|
||||||
|
int bytes, BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
r = blk_check_byte_request(blk_in, off_in, bytes);
|
||||||
|
if (r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
r = blk_check_byte_request(blk_out, off_out, bytes);
|
||||||
|
if (r) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
return bdrv_co_copy_range(blk_in->root, off_in,
|
||||||
|
blk_out->root, off_out,
|
||||||
|
bytes, flags);
|
||||||
|
}
|
||||||
|
|
|
@ -59,6 +59,7 @@
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
#include <linux/cdrom.h>
|
#include <linux/cdrom.h>
|
||||||
#include <linux/fd.h>
|
#include <linux/fd.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
@ -187,6 +188,8 @@ typedef struct RawPosixAIOData {
|
||||||
#define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */
|
#define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */
|
||||||
off_t aio_offset;
|
off_t aio_offset;
|
||||||
int aio_type;
|
int aio_type;
|
||||||
|
int aio_fd2;
|
||||||
|
off_t aio_offset2;
|
||||||
} RawPosixAIOData;
|
} RawPosixAIOData;
|
||||||
|
|
||||||
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
|
||||||
|
@ -1446,6 +1449,49 @@ static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb)
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef HAVE_COPY_FILE_RANGE
|
||||||
|
static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd,
|
||||||
|
off_t *out_off, size_t len, unsigned int flags)
|
||||||
|
{
|
||||||
|
#ifdef __NR_copy_file_range
|
||||||
|
return syscall(__NR_copy_file_range, in_fd, in_off, out_fd,
|
||||||
|
out_off, len, flags);
|
||||||
|
#else
|
||||||
|
errno = ENOSYS;
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static ssize_t handle_aiocb_copy_range(RawPosixAIOData *aiocb)
|
||||||
|
{
|
||||||
|
uint64_t bytes = aiocb->aio_nbytes;
|
||||||
|
off_t in_off = aiocb->aio_offset;
|
||||||
|
off_t out_off = aiocb->aio_offset2;
|
||||||
|
|
||||||
|
while (bytes) {
|
||||||
|
ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off,
|
||||||
|
aiocb->aio_fd2, &out_off,
|
||||||
|
bytes, 0);
|
||||||
|
if (ret == -EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == ENOSYS) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
} else {
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ret) {
|
||||||
|
/* No progress (e.g. when beyond EOF), fall back to buffer I/O. */
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
bytes -= ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb)
|
static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb)
|
||||||
{
|
{
|
||||||
int ret = -EOPNOTSUPP;
|
int ret = -EOPNOTSUPP;
|
||||||
|
@ -1526,6 +1572,9 @@ static int aio_worker(void *arg)
|
||||||
case QEMU_AIO_WRITE_ZEROES:
|
case QEMU_AIO_WRITE_ZEROES:
|
||||||
ret = handle_aiocb_write_zeroes(aiocb);
|
ret = handle_aiocb_write_zeroes(aiocb);
|
||||||
break;
|
break;
|
||||||
|
case QEMU_AIO_COPY_RANGE:
|
||||||
|
ret = handle_aiocb_copy_range(aiocb);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
|
fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
|
@ -1536,9 +1585,10 @@ static int aio_worker(void *arg)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int paio_submit_co(BlockDriverState *bs, int fd,
|
static int paio_submit_co_full(BlockDriverState *bs, int fd,
|
||||||
int64_t offset, QEMUIOVector *qiov,
|
int64_t offset, int fd2, int64_t offset2,
|
||||||
int bytes, int type)
|
QEMUIOVector *qiov,
|
||||||
|
int bytes, int type)
|
||||||
{
|
{
|
||||||
RawPosixAIOData *acb = g_new(RawPosixAIOData, 1);
|
RawPosixAIOData *acb = g_new(RawPosixAIOData, 1);
|
||||||
ThreadPool *pool;
|
ThreadPool *pool;
|
||||||
|
@ -1546,6 +1596,8 @@ static int paio_submit_co(BlockDriverState *bs, int fd,
|
||||||
acb->bs = bs;
|
acb->bs = bs;
|
||||||
acb->aio_type = type;
|
acb->aio_type = type;
|
||||||
acb->aio_fildes = fd;
|
acb->aio_fildes = fd;
|
||||||
|
acb->aio_fd2 = fd2;
|
||||||
|
acb->aio_offset2 = offset2;
|
||||||
|
|
||||||
acb->aio_nbytes = bytes;
|
acb->aio_nbytes = bytes;
|
||||||
acb->aio_offset = offset;
|
acb->aio_offset = offset;
|
||||||
|
@ -1561,6 +1613,13 @@ static int paio_submit_co(BlockDriverState *bs, int fd,
|
||||||
return thread_pool_submit_co(pool, aio_worker, acb);
|
return thread_pool_submit_co(pool, aio_worker, acb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int paio_submit_co(BlockDriverState *bs, int fd,
|
||||||
|
int64_t offset, QEMUIOVector *qiov,
|
||||||
|
int bytes, int type)
|
||||||
|
{
|
||||||
|
return paio_submit_co_full(bs, fd, offset, -1, 0, qiov, bytes, type);
|
||||||
|
}
|
||||||
|
|
||||||
static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd,
|
||||||
int64_t offset, QEMUIOVector *qiov, int bytes,
|
int64_t offset, QEMUIOVector *qiov, int bytes,
|
||||||
BlockCompletionFunc *cb, void *opaque, int type)
|
BlockCompletionFunc *cb, void *opaque, int type)
|
||||||
|
@ -2451,6 +2510,35 @@ static void raw_abort_perm_update(BlockDriverState *bs)
|
||||||
raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
|
raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs,
|
||||||
|
BdrvChild *src, uint64_t src_offset,
|
||||||
|
BdrvChild *dst, uint64_t dst_offset,
|
||||||
|
uint64_t bytes, BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs,
|
||||||
|
BdrvChild *src, uint64_t src_offset,
|
||||||
|
BdrvChild *dst, uint64_t dst_offset,
|
||||||
|
uint64_t bytes, BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
BDRVRawState *s = bs->opaque;
|
||||||
|
BDRVRawState *src_s;
|
||||||
|
|
||||||
|
assert(dst->bs == bs);
|
||||||
|
if (src->bs->drv->bdrv_co_copy_range_to != raw_co_copy_range_to) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
src_s = src->bs->opaque;
|
||||||
|
if (fd_open(bs) < 0 || fd_open(bs) < 0) {
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
return paio_submit_co_full(bs, src_s->fd, src_offset, s->fd, dst_offset,
|
||||||
|
NULL, bytes, QEMU_AIO_COPY_RANGE);
|
||||||
|
}
|
||||||
|
|
||||||
BlockDriver bdrv_file = {
|
BlockDriver bdrv_file = {
|
||||||
.format_name = "file",
|
.format_name = "file",
|
||||||
.protocol_name = "file",
|
.protocol_name = "file",
|
||||||
|
@ -2474,6 +2562,8 @@ BlockDriver bdrv_file = {
|
||||||
.bdrv_co_pwritev = raw_co_pwritev,
|
.bdrv_co_pwritev = raw_co_pwritev,
|
||||||
.bdrv_aio_flush = raw_aio_flush,
|
.bdrv_aio_flush = raw_aio_flush,
|
||||||
.bdrv_aio_pdiscard = raw_aio_pdiscard,
|
.bdrv_aio_pdiscard = raw_aio_pdiscard,
|
||||||
|
.bdrv_co_copy_range_from = raw_co_copy_range_from,
|
||||||
|
.bdrv_co_copy_range_to = raw_co_copy_range_to,
|
||||||
.bdrv_refresh_limits = raw_refresh_limits,
|
.bdrv_refresh_limits = raw_refresh_limits,
|
||||||
.bdrv_io_plug = raw_aio_plug,
|
.bdrv_io_plug = raw_aio_plug,
|
||||||
.bdrv_io_unplug = raw_aio_unplug,
|
.bdrv_io_unplug = raw_aio_unplug,
|
||||||
|
@ -2952,6 +3042,8 @@ static BlockDriver bdrv_host_device = {
|
||||||
.bdrv_co_pwritev = raw_co_pwritev,
|
.bdrv_co_pwritev = raw_co_pwritev,
|
||||||
.bdrv_aio_flush = raw_aio_flush,
|
.bdrv_aio_flush = raw_aio_flush,
|
||||||
.bdrv_aio_pdiscard = hdev_aio_pdiscard,
|
.bdrv_aio_pdiscard = hdev_aio_pdiscard,
|
||||||
|
.bdrv_co_copy_range_from = raw_co_copy_range_from,
|
||||||
|
.bdrv_co_copy_range_to = raw_co_copy_range_to,
|
||||||
.bdrv_refresh_limits = raw_refresh_limits,
|
.bdrv_refresh_limits = raw_refresh_limits,
|
||||||
.bdrv_io_plug = raw_aio_plug,
|
.bdrv_io_plug = raw_aio_plug,
|
||||||
.bdrv_io_unplug = raw_aio_unplug,
|
.bdrv_io_unplug = raw_aio_unplug,
|
||||||
|
|
97
block/io.c
97
block/io.c
|
@ -2835,3 +2835,100 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host)
|
||||||
bdrv_unregister_buf(child->bs, host);
|
bdrv_unregister_buf(child->bs, host);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src,
|
||||||
|
uint64_t src_offset,
|
||||||
|
BdrvChild *dst,
|
||||||
|
uint64_t dst_offset,
|
||||||
|
uint64_t bytes,
|
||||||
|
BdrvRequestFlags flags,
|
||||||
|
bool recurse_src)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!src || !dst || !src->bs || !dst->bs) {
|
||||||
|
return -ENOMEDIUM;
|
||||||
|
}
|
||||||
|
ret = bdrv_check_byte_request(src->bs, src_offset, bytes);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bdrv_check_byte_request(dst->bs, dst_offset, bytes);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (flags & BDRV_REQ_ZERO_WRITE) {
|
||||||
|
return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!src->bs->drv->bdrv_co_copy_range_from
|
||||||
|
|| !dst->bs->drv->bdrv_co_copy_range_to
|
||||||
|
|| src->bs->encrypted || dst->bs->encrypted) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
if (recurse_src) {
|
||||||
|
return src->bs->drv->bdrv_co_copy_range_from(src->bs,
|
||||||
|
src, src_offset,
|
||||||
|
dst, dst_offset,
|
||||||
|
bytes, flags);
|
||||||
|
} else {
|
||||||
|
return dst->bs->drv->bdrv_co_copy_range_to(dst->bs,
|
||||||
|
src, src_offset,
|
||||||
|
dst, dst_offset,
|
||||||
|
bytes, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy range from @src to @dst.
|
||||||
|
*
|
||||||
|
* See the comment of bdrv_co_copy_range for the parameter and return value
|
||||||
|
* semantics. */
|
||||||
|
int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset,
|
||||||
|
BdrvChild *dst, uint64_t dst_offset,
|
||||||
|
uint64_t bytes, BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset,
|
||||||
|
bytes, flags, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy range from @src to @dst.
|
||||||
|
*
|
||||||
|
* See the comment of bdrv_co_copy_range for the parameter and return value
|
||||||
|
* semantics. */
|
||||||
|
int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset,
|
||||||
|
BdrvChild *dst, uint64_t dst_offset,
|
||||||
|
uint64_t bytes, BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
return bdrv_co_copy_range_internal(src, src_offset, dst, dst_offset,
|
||||||
|
bytes, flags, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset,
|
||||||
|
BdrvChild *dst, uint64_t dst_offset,
|
||||||
|
uint64_t bytes, BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
BdrvTrackedRequest src_req, dst_req;
|
||||||
|
BlockDriverState *src_bs = src->bs;
|
||||||
|
BlockDriverState *dst_bs = dst->bs;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
bdrv_inc_in_flight(src_bs);
|
||||||
|
bdrv_inc_in_flight(dst_bs);
|
||||||
|
tracked_request_begin(&src_req, src_bs, src_offset,
|
||||||
|
bytes, BDRV_TRACKED_READ);
|
||||||
|
tracked_request_begin(&dst_req, dst_bs, dst_offset,
|
||||||
|
bytes, BDRV_TRACKED_WRITE);
|
||||||
|
|
||||||
|
wait_serialising_requests(&src_req);
|
||||||
|
wait_serialising_requests(&dst_req);
|
||||||
|
ret = bdrv_co_copy_range_from(src, src_offset,
|
||||||
|
dst, dst_offset,
|
||||||
|
bytes, flags);
|
||||||
|
|
||||||
|
tracked_request_end(&src_req);
|
||||||
|
tracked_request_end(&dst_req);
|
||||||
|
bdrv_dec_in_flight(src_bs);
|
||||||
|
bdrv_dec_in_flight(dst_bs);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
314
block/iscsi.c
314
block/iscsi.c
|
@ -68,6 +68,7 @@ typedef struct IscsiLun {
|
||||||
QemuMutex mutex;
|
QemuMutex mutex;
|
||||||
struct scsi_inquiry_logical_block_provisioning lbp;
|
struct scsi_inquiry_logical_block_provisioning lbp;
|
||||||
struct scsi_inquiry_block_limits bl;
|
struct scsi_inquiry_block_limits bl;
|
||||||
|
struct scsi_inquiry_device_designator *dd;
|
||||||
unsigned char *zeroblock;
|
unsigned char *zeroblock;
|
||||||
/* The allocmap tracks which clusters (pages) on the iSCSI target are
|
/* The allocmap tracks which clusters (pages) on the iSCSI target are
|
||||||
* allocated and which are not. In case a target returns zeros for
|
* allocated and which are not. In case a target returns zeros for
|
||||||
|
@ -555,6 +556,17 @@ static inline bool iscsi_allocmap_is_valid(IscsiLun *iscsilun,
|
||||||
offset / iscsilun->cluster_size) == size);
|
offset / iscsilun->cluster_size) == size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void coroutine_fn iscsi_co_wait_for_task(IscsiTask *iTask,
|
||||||
|
IscsiLun *iscsilun)
|
||||||
|
{
|
||||||
|
while (!iTask->complete) {
|
||||||
|
iscsi_set_events(iscsilun);
|
||||||
|
qemu_mutex_unlock(&iscsilun->mutex);
|
||||||
|
qemu_coroutine_yield();
|
||||||
|
qemu_mutex_lock(&iscsilun->mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int coroutine_fn
|
static int coroutine_fn
|
||||||
iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||||
QEMUIOVector *iov, int flags)
|
QEMUIOVector *iov, int flags)
|
||||||
|
@ -616,12 +628,7 @@ retry:
|
||||||
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
|
scsi_task_set_iov_out(iTask.task, (struct scsi_iovec *) iov->iov,
|
||||||
iov->niov);
|
iov->niov);
|
||||||
#endif
|
#endif
|
||||||
while (!iTask.complete) {
|
iscsi_co_wait_for_task(&iTask, iscsilun);
|
||||||
iscsi_set_events(iscsilun);
|
|
||||||
qemu_mutex_unlock(&iscsilun->mutex);
|
|
||||||
qemu_coroutine_yield();
|
|
||||||
qemu_mutex_lock(&iscsilun->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iTask.task != NULL) {
|
if (iTask.task != NULL) {
|
||||||
scsi_free_scsi_task(iTask.task);
|
scsi_free_scsi_task(iTask.task);
|
||||||
|
@ -692,13 +699,7 @@ retry:
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
iscsi_co_wait_for_task(&iTask, iscsilun);
|
||||||
while (!iTask.complete) {
|
|
||||||
iscsi_set_events(iscsilun);
|
|
||||||
qemu_mutex_unlock(&iscsilun->mutex);
|
|
||||||
qemu_coroutine_yield();
|
|
||||||
qemu_mutex_lock(&iscsilun->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iTask.do_retry) {
|
if (iTask.do_retry) {
|
||||||
if (iTask.task != NULL) {
|
if (iTask.task != NULL) {
|
||||||
|
@ -862,13 +863,8 @@ retry:
|
||||||
#if LIBISCSI_API_VERSION < (20160603)
|
#if LIBISCSI_API_VERSION < (20160603)
|
||||||
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
|
scsi_task_set_iov_in(iTask.task, (struct scsi_iovec *) iov->iov, iov->niov);
|
||||||
#endif
|
#endif
|
||||||
while (!iTask.complete) {
|
|
||||||
iscsi_set_events(iscsilun);
|
|
||||||
qemu_mutex_unlock(&iscsilun->mutex);
|
|
||||||
qemu_coroutine_yield();
|
|
||||||
qemu_mutex_lock(&iscsilun->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
iscsi_co_wait_for_task(&iTask, iscsilun);
|
||||||
if (iTask.task != NULL) {
|
if (iTask.task != NULL) {
|
||||||
scsi_free_scsi_task(iTask.task);
|
scsi_free_scsi_task(iTask.task);
|
||||||
iTask.task = NULL;
|
iTask.task = NULL;
|
||||||
|
@ -905,12 +901,7 @@ retry:
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!iTask.complete) {
|
iscsi_co_wait_for_task(&iTask, iscsilun);
|
||||||
iscsi_set_events(iscsilun);
|
|
||||||
qemu_mutex_unlock(&iscsilun->mutex);
|
|
||||||
qemu_coroutine_yield();
|
|
||||||
qemu_mutex_lock(&iscsilun->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iTask.task != NULL) {
|
if (iTask.task != NULL) {
|
||||||
scsi_free_scsi_task(iTask.task);
|
scsi_free_scsi_task(iTask.task);
|
||||||
|
@ -1142,12 +1133,7 @@ retry:
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!iTask.complete) {
|
iscsi_co_wait_for_task(&iTask, iscsilun);
|
||||||
iscsi_set_events(iscsilun);
|
|
||||||
qemu_mutex_unlock(&iscsilun->mutex);
|
|
||||||
qemu_coroutine_yield();
|
|
||||||
qemu_mutex_lock(&iscsilun->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iTask.task != NULL) {
|
if (iTask.task != NULL) {
|
||||||
scsi_free_scsi_task(iTask.task);
|
scsi_free_scsi_task(iTask.task);
|
||||||
|
@ -1243,12 +1229,7 @@ retry:
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!iTask.complete) {
|
iscsi_co_wait_for_task(&iTask, iscsilun);
|
||||||
iscsi_set_events(iscsilun);
|
|
||||||
qemu_mutex_unlock(&iscsilun->mutex);
|
|
||||||
qemu_coroutine_yield();
|
|
||||||
qemu_mutex_lock(&iscsilun->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iTask.status == SCSI_STATUS_CHECK_CONDITION &&
|
if (iTask.status == SCSI_STATUS_CHECK_CONDITION &&
|
||||||
iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
|
iTask.task->sense.key == SCSI_SENSE_ILLEGAL_REQUEST &&
|
||||||
|
@ -1740,6 +1721,30 @@ static QemuOptsList runtime_opts = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void iscsi_save_designator(IscsiLun *lun,
|
||||||
|
struct scsi_inquiry_device_identification *inq_di)
|
||||||
|
{
|
||||||
|
struct scsi_inquiry_device_designator *desig, *copy = NULL;
|
||||||
|
|
||||||
|
for (desig = inq_di->designators; desig; desig = desig->next) {
|
||||||
|
if (desig->association ||
|
||||||
|
desig->designator_type > SCSI_DESIGNATOR_TYPE_NAA) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* NAA works better than T10 vendor ID based designator. */
|
||||||
|
if (!copy || copy->designator_type < desig->designator_type) {
|
||||||
|
copy = desig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (copy) {
|
||||||
|
lun->dd = g_new(struct scsi_inquiry_device_designator, 1);
|
||||||
|
*lun->dd = *copy;
|
||||||
|
lun->dd->next = NULL;
|
||||||
|
lun->dd->designator = g_malloc(copy->designator_length);
|
||||||
|
memcpy(lun->dd->designator, copy->designator, copy->designator_length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -1922,6 +1927,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
struct scsi_task *inq_task;
|
struct scsi_task *inq_task;
|
||||||
struct scsi_inquiry_logical_block_provisioning *inq_lbp;
|
struct scsi_inquiry_logical_block_provisioning *inq_lbp;
|
||||||
struct scsi_inquiry_block_limits *inq_bl;
|
struct scsi_inquiry_block_limits *inq_bl;
|
||||||
|
struct scsi_inquiry_device_identification *inq_di;
|
||||||
switch (inq_vpd->pages[i]) {
|
switch (inq_vpd->pages[i]) {
|
||||||
case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
|
case SCSI_INQUIRY_PAGECODE_LOGICAL_BLOCK_PROVISIONING:
|
||||||
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
||||||
|
@ -1947,6 +1953,17 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
sizeof(struct scsi_inquiry_block_limits));
|
sizeof(struct scsi_inquiry_block_limits));
|
||||||
scsi_free_scsi_task(inq_task);
|
scsi_free_scsi_task(inq_task);
|
||||||
break;
|
break;
|
||||||
|
case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
|
||||||
|
inq_task = iscsi_do_inquiry(iscsilun->iscsi, iscsilun->lun, 1,
|
||||||
|
SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION,
|
||||||
|
(void **) &inq_di, errp);
|
||||||
|
if (inq_task == NULL) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
iscsi_save_designator(iscsilun, inq_di);
|
||||||
|
scsi_free_scsi_task(inq_task);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2003,6 +2020,10 @@ static void iscsi_close(BlockDriverState *bs)
|
||||||
iscsi_logout_sync(iscsi);
|
iscsi_logout_sync(iscsi);
|
||||||
}
|
}
|
||||||
iscsi_destroy_context(iscsi);
|
iscsi_destroy_context(iscsi);
|
||||||
|
if (iscsilun->dd) {
|
||||||
|
g_free(iscsilun->dd->designator);
|
||||||
|
g_free(iscsilun->dd);
|
||||||
|
}
|
||||||
g_free(iscsilun->zeroblock);
|
g_free(iscsilun->zeroblock);
|
||||||
iscsi_allocmap_free(iscsilun);
|
iscsi_allocmap_free(iscsilun);
|
||||||
qemu_mutex_destroy(&iscsilun->mutex);
|
qemu_mutex_destroy(&iscsilun->mutex);
|
||||||
|
@ -2184,6 +2205,221 @@ static void coroutine_fn iscsi_co_invalidate_cache(BlockDriverState *bs,
|
||||||
iscsi_allocmap_invalidate(iscsilun);
|
iscsi_allocmap_invalidate(iscsilun);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn iscsi_co_copy_range_from(BlockDriverState *bs,
|
||||||
|
BdrvChild *src,
|
||||||
|
uint64_t src_offset,
|
||||||
|
BdrvChild *dst,
|
||||||
|
uint64_t dst_offset,
|
||||||
|
uint64_t bytes,
|
||||||
|
BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
return bdrv_co_copy_range_to(src, src_offset, dst, dst_offset, bytes, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct scsi_task *iscsi_xcopy_task(int param_len)
|
||||||
|
{
|
||||||
|
struct scsi_task *task;
|
||||||
|
|
||||||
|
task = g_new0(struct scsi_task, 1);
|
||||||
|
|
||||||
|
task->cdb[0] = EXTENDED_COPY;
|
||||||
|
task->cdb[10] = (param_len >> 24) & 0xFF;
|
||||||
|
task->cdb[11] = (param_len >> 16) & 0xFF;
|
||||||
|
task->cdb[12] = (param_len >> 8) & 0xFF;
|
||||||
|
task->cdb[13] = param_len & 0xFF;
|
||||||
|
task->cdb_size = 16;
|
||||||
|
task->xfer_dir = SCSI_XFER_WRITE;
|
||||||
|
task->expxferlen = param_len;
|
||||||
|
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iscsi_populate_target_desc(unsigned char *desc, IscsiLun *lun)
|
||||||
|
{
|
||||||
|
struct scsi_inquiry_device_designator *dd = lun->dd;
|
||||||
|
|
||||||
|
memset(desc, 0, 32);
|
||||||
|
desc[0] = 0xE4; /* IDENT_DESCR_TGT_DESCR */
|
||||||
|
desc[4] = dd->code_set;
|
||||||
|
desc[5] = (dd->designator_type & 0xF)
|
||||||
|
| ((dd->association & 3) << 4);
|
||||||
|
desc[7] = dd->designator_length;
|
||||||
|
memcpy(desc + 8, dd->designator, dd->designator_length);
|
||||||
|
|
||||||
|
desc[28] = 0;
|
||||||
|
desc[29] = (lun->block_size >> 16) & 0xFF;
|
||||||
|
desc[30] = (lun->block_size >> 8) & 0xFF;
|
||||||
|
desc[31] = lun->block_size & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iscsi_xcopy_desc_hdr(uint8_t *hdr, int dc, int cat, int src_index,
|
||||||
|
int dst_index)
|
||||||
|
{
|
||||||
|
hdr[0] = 0x02; /* BLK_TO_BLK_SEG_DESCR */
|
||||||
|
hdr[1] = ((dc << 1) | cat) & 0xFF;
|
||||||
|
hdr[2] = (XCOPY_BLK2BLK_SEG_DESC_SIZE >> 8) & 0xFF;
|
||||||
|
/* don't account for the first 4 bytes in descriptor header*/
|
||||||
|
hdr[3] = (XCOPY_BLK2BLK_SEG_DESC_SIZE - 4 /* SEG_DESC_SRC_INDEX_OFFSET */) & 0xFF;
|
||||||
|
hdr[4] = (src_index >> 8) & 0xFF;
|
||||||
|
hdr[5] = src_index & 0xFF;
|
||||||
|
hdr[6] = (dst_index >> 8) & 0xFF;
|
||||||
|
hdr[7] = dst_index & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iscsi_xcopy_populate_desc(uint8_t *desc, int dc, int cat,
|
||||||
|
int src_index, int dst_index, int num_blks,
|
||||||
|
uint64_t src_lba, uint64_t dst_lba)
|
||||||
|
{
|
||||||
|
iscsi_xcopy_desc_hdr(desc, dc, cat, src_index, dst_index);
|
||||||
|
|
||||||
|
/* The caller should verify the request size */
|
||||||
|
assert(num_blks < 65536);
|
||||||
|
desc[10] = (num_blks >> 8) & 0xFF;
|
||||||
|
desc[11] = num_blks & 0xFF;
|
||||||
|
desc[12] = (src_lba >> 56) & 0xFF;
|
||||||
|
desc[13] = (src_lba >> 48) & 0xFF;
|
||||||
|
desc[14] = (src_lba >> 40) & 0xFF;
|
||||||
|
desc[15] = (src_lba >> 32) & 0xFF;
|
||||||
|
desc[16] = (src_lba >> 24) & 0xFF;
|
||||||
|
desc[17] = (src_lba >> 16) & 0xFF;
|
||||||
|
desc[18] = (src_lba >> 8) & 0xFF;
|
||||||
|
desc[19] = src_lba & 0xFF;
|
||||||
|
desc[20] = (dst_lba >> 56) & 0xFF;
|
||||||
|
desc[21] = (dst_lba >> 48) & 0xFF;
|
||||||
|
desc[22] = (dst_lba >> 40) & 0xFF;
|
||||||
|
desc[23] = (dst_lba >> 32) & 0xFF;
|
||||||
|
desc[24] = (dst_lba >> 24) & 0xFF;
|
||||||
|
desc[25] = (dst_lba >> 16) & 0xFF;
|
||||||
|
desc[26] = (dst_lba >> 8) & 0xFF;
|
||||||
|
desc[27] = dst_lba & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iscsi_xcopy_populate_header(unsigned char *buf, int list_id, int str,
|
||||||
|
int list_id_usage, int prio,
|
||||||
|
int tgt_desc_len,
|
||||||
|
int seg_desc_len, int inline_data_len)
|
||||||
|
{
|
||||||
|
buf[0] = list_id;
|
||||||
|
buf[1] = ((str & 1) << 5) | ((list_id_usage & 3) << 3) | (prio & 7);
|
||||||
|
buf[2] = (tgt_desc_len >> 8) & 0xFF;
|
||||||
|
buf[3] = tgt_desc_len & 0xFF;
|
||||||
|
buf[8] = (seg_desc_len >> 24) & 0xFF;
|
||||||
|
buf[9] = (seg_desc_len >> 16) & 0xFF;
|
||||||
|
buf[10] = (seg_desc_len >> 8) & 0xFF;
|
||||||
|
buf[11] = seg_desc_len & 0xFF;
|
||||||
|
buf[12] = (inline_data_len >> 24) & 0xFF;
|
||||||
|
buf[13] = (inline_data_len >> 16) & 0xFF;
|
||||||
|
buf[14] = (inline_data_len >> 8) & 0xFF;
|
||||||
|
buf[15] = inline_data_len & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void iscsi_xcopy_data(struct iscsi_data *data,
|
||||||
|
IscsiLun *src, int64_t src_lba,
|
||||||
|
IscsiLun *dst, int64_t dst_lba,
|
||||||
|
uint16_t num_blocks)
|
||||||
|
{
|
||||||
|
uint8_t *buf;
|
||||||
|
const int src_offset = XCOPY_DESC_OFFSET;
|
||||||
|
const int dst_offset = XCOPY_DESC_OFFSET + IDENT_DESCR_TGT_DESCR_SIZE;
|
||||||
|
const int seg_offset = dst_offset + IDENT_DESCR_TGT_DESCR_SIZE;
|
||||||
|
|
||||||
|
data->size = XCOPY_DESC_OFFSET +
|
||||||
|
IDENT_DESCR_TGT_DESCR_SIZE * 2 +
|
||||||
|
XCOPY_BLK2BLK_SEG_DESC_SIZE;
|
||||||
|
data->data = g_malloc0(data->size);
|
||||||
|
buf = data->data;
|
||||||
|
|
||||||
|
/* Initialise the parameter list header */
|
||||||
|
iscsi_xcopy_populate_header(buf, 1, 0, 2 /* LIST_ID_USAGE_DISCARD */,
|
||||||
|
0, 2 * IDENT_DESCR_TGT_DESCR_SIZE,
|
||||||
|
XCOPY_BLK2BLK_SEG_DESC_SIZE,
|
||||||
|
0);
|
||||||
|
|
||||||
|
/* Initialise CSCD list with one src + one dst descriptor */
|
||||||
|
iscsi_populate_target_desc(&buf[src_offset], src);
|
||||||
|
iscsi_populate_target_desc(&buf[dst_offset], dst);
|
||||||
|
|
||||||
|
/* Initialise one segment descriptor */
|
||||||
|
iscsi_xcopy_populate_desc(&buf[seg_offset], 0, 0, 0, 1, num_blocks,
|
||||||
|
src_lba, dst_lba);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn iscsi_co_copy_range_to(BlockDriverState *bs,
|
||||||
|
BdrvChild *src,
|
||||||
|
uint64_t src_offset,
|
||||||
|
BdrvChild *dst,
|
||||||
|
uint64_t dst_offset,
|
||||||
|
uint64_t bytes,
|
||||||
|
BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
IscsiLun *dst_lun = dst->bs->opaque;
|
||||||
|
IscsiLun *src_lun;
|
||||||
|
struct IscsiTask iscsi_task;
|
||||||
|
struct iscsi_data data;
|
||||||
|
int r = 0;
|
||||||
|
int block_size;
|
||||||
|
|
||||||
|
if (src->bs->drv->bdrv_co_copy_range_to != iscsi_co_copy_range_to) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
src_lun = src->bs->opaque;
|
||||||
|
|
||||||
|
if (!src_lun->dd || !dst_lun->dd) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
if (!is_byte_request_lun_aligned(dst_offset, bytes, dst_lun)) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
if (!is_byte_request_lun_aligned(src_offset, bytes, src_lun)) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
if (dst_lun->block_size != src_lun->block_size ||
|
||||||
|
!dst_lun->block_size) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
block_size = dst_lun->block_size;
|
||||||
|
if (bytes / block_size > 65535) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
iscsi_xcopy_data(&data,
|
||||||
|
src_lun, src_offset / block_size,
|
||||||
|
dst_lun, dst_offset / block_size,
|
||||||
|
bytes / block_size);
|
||||||
|
|
||||||
|
iscsi_co_init_iscsitask(dst_lun, &iscsi_task);
|
||||||
|
|
||||||
|
qemu_mutex_lock(&dst_lun->mutex);
|
||||||
|
iscsi_task.task = iscsi_xcopy_task(data.size);
|
||||||
|
retry:
|
||||||
|
if (iscsi_scsi_command_async(dst_lun->iscsi, dst_lun->lun,
|
||||||
|
iscsi_task.task, iscsi_co_generic_cb,
|
||||||
|
&data,
|
||||||
|
&iscsi_task) != 0) {
|
||||||
|
r = -EIO;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
iscsi_co_wait_for_task(&iscsi_task, dst_lun);
|
||||||
|
|
||||||
|
if (iscsi_task.do_retry) {
|
||||||
|
iscsi_task.complete = 0;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iscsi_task.status != SCSI_STATUS_GOOD) {
|
||||||
|
r = iscsi_task.err_code;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
g_free(iscsi_task.task);
|
||||||
|
qemu_mutex_unlock(&dst_lun->mutex);
|
||||||
|
g_free(iscsi_task.err_str);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
static QemuOptsList iscsi_create_opts = {
|
static QemuOptsList iscsi_create_opts = {
|
||||||
.name = "iscsi-create-opts",
|
.name = "iscsi-create-opts",
|
||||||
.head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head),
|
.head = QTAILQ_HEAD_INITIALIZER(iscsi_create_opts.head),
|
||||||
|
@ -2218,6 +2454,8 @@ static BlockDriver bdrv_iscsi = {
|
||||||
|
|
||||||
.bdrv_co_block_status = iscsi_co_block_status,
|
.bdrv_co_block_status = iscsi_co_block_status,
|
||||||
.bdrv_co_pdiscard = iscsi_co_pdiscard,
|
.bdrv_co_pdiscard = iscsi_co_pdiscard,
|
||||||
|
.bdrv_co_copy_range_from = iscsi_co_copy_range_from,
|
||||||
|
.bdrv_co_copy_range_to = iscsi_co_copy_range_to,
|
||||||
.bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
|
.bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
|
||||||
.bdrv_co_readv = iscsi_co_readv,
|
.bdrv_co_readv = iscsi_co_readv,
|
||||||
.bdrv_co_writev = iscsi_co_writev,
|
.bdrv_co_writev = iscsi_co_writev,
|
||||||
|
@ -2253,6 +2491,8 @@ static BlockDriver bdrv_iser = {
|
||||||
|
|
||||||
.bdrv_co_block_status = iscsi_co_block_status,
|
.bdrv_co_block_status = iscsi_co_block_status,
|
||||||
.bdrv_co_pdiscard = iscsi_co_pdiscard,
|
.bdrv_co_pdiscard = iscsi_co_pdiscard,
|
||||||
|
.bdrv_co_copy_range_from = iscsi_co_copy_range_from,
|
||||||
|
.bdrv_co_copy_range_to = iscsi_co_copy_range_to,
|
||||||
.bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
|
.bdrv_co_pwrite_zeroes = iscsi_co_pwrite_zeroes,
|
||||||
.bdrv_co_readv = iscsi_co_readv,
|
.bdrv_co_readv = iscsi_co_readv,
|
||||||
.bdrv_co_writev = iscsi_co_writev,
|
.bdrv_co_writev = iscsi_co_writev,
|
||||||
|
|
229
block/qcow2.c
229
block/qcow2.c
|
@ -1761,6 +1761,39 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs,
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static coroutine_fn int qcow2_handle_l2meta(BlockDriverState *bs,
|
||||||
|
QCowL2Meta **pl2meta,
|
||||||
|
bool link_l2)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
QCowL2Meta *l2meta = *pl2meta;
|
||||||
|
|
||||||
|
while (l2meta != NULL) {
|
||||||
|
QCowL2Meta *next;
|
||||||
|
|
||||||
|
if (!ret && link_l2) {
|
||||||
|
ret = qcow2_alloc_cluster_link_l2(bs, l2meta);
|
||||||
|
if (ret) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Take the request off the list of running requests */
|
||||||
|
if (l2meta->nb_clusters != 0) {
|
||||||
|
QLIST_REMOVE(l2meta, next_in_flight);
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
||||||
|
|
||||||
|
next = l2meta->next;
|
||||||
|
g_free(l2meta);
|
||||||
|
l2meta = next;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
*pl2meta = l2meta;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
|
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||||
uint64_t bytes, QEMUIOVector *qiov,
|
uint64_t bytes, QEMUIOVector *qiov,
|
||||||
int flags)
|
int flags)
|
||||||
|
@ -2047,24 +2080,9 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (l2meta != NULL) {
|
ret = qcow2_handle_l2meta(bs, &l2meta, true);
|
||||||
QCowL2Meta *next;
|
if (ret) {
|
||||||
|
goto fail;
|
||||||
ret = qcow2_alloc_cluster_link_l2(bs, l2meta);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Take the request off the list of running requests */
|
|
||||||
if (l2meta->nb_clusters != 0) {
|
|
||||||
QLIST_REMOVE(l2meta, next_in_flight);
|
|
||||||
}
|
|
||||||
|
|
||||||
qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
|
||||||
|
|
||||||
next = l2meta->next;
|
|
||||||
g_free(l2meta);
|
|
||||||
l2meta = next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes -= cur_bytes;
|
bytes -= cur_bytes;
|
||||||
|
@ -2075,18 +2093,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
while (l2meta != NULL) {
|
qcow2_handle_l2meta(bs, &l2meta, false);
|
||||||
QCowL2Meta *next;
|
|
||||||
|
|
||||||
if (l2meta->nb_clusters != 0) {
|
|
||||||
QLIST_REMOVE(l2meta, next_in_flight);
|
|
||||||
}
|
|
||||||
qemu_co_queue_restart_all(&l2meta->dependent_requests);
|
|
||||||
|
|
||||||
next = l2meta->next;
|
|
||||||
g_free(l2meta);
|
|
||||||
l2meta = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
|
|
||||||
|
@ -3273,6 +3280,166 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn
|
||||||
|
qcow2_co_copy_range_from(BlockDriverState *bs,
|
||||||
|
BdrvChild *src, uint64_t src_offset,
|
||||||
|
BdrvChild *dst, uint64_t dst_offset,
|
||||||
|
uint64_t bytes, BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
int ret;
|
||||||
|
unsigned int cur_bytes; /* number of bytes in current iteration */
|
||||||
|
BdrvChild *child = NULL;
|
||||||
|
BdrvRequestFlags cur_flags;
|
||||||
|
|
||||||
|
assert(!bs->encrypted);
|
||||||
|
qemu_co_mutex_lock(&s->lock);
|
||||||
|
|
||||||
|
while (bytes != 0) {
|
||||||
|
uint64_t copy_offset = 0;
|
||||||
|
/* prepare next request */
|
||||||
|
cur_bytes = MIN(bytes, INT_MAX);
|
||||||
|
cur_flags = flags;
|
||||||
|
|
||||||
|
ret = qcow2_get_cluster_offset(bs, src_offset, &cur_bytes, ©_offset);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (ret) {
|
||||||
|
case QCOW2_CLUSTER_UNALLOCATED:
|
||||||
|
if (bs->backing && bs->backing->bs) {
|
||||||
|
int64_t backing_length = bdrv_getlength(bs->backing->bs);
|
||||||
|
if (src_offset >= backing_length) {
|
||||||
|
cur_flags |= BDRV_REQ_ZERO_WRITE;
|
||||||
|
} else {
|
||||||
|
child = bs->backing;
|
||||||
|
cur_bytes = MIN(cur_bytes, backing_length - src_offset);
|
||||||
|
copy_offset = src_offset;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cur_flags |= BDRV_REQ_ZERO_WRITE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QCOW2_CLUSTER_ZERO_PLAIN:
|
||||||
|
case QCOW2_CLUSTER_ZERO_ALLOC:
|
||||||
|
cur_flags |= BDRV_REQ_ZERO_WRITE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QCOW2_CLUSTER_COMPRESSED:
|
||||||
|
ret = -ENOTSUP;
|
||||||
|
goto out;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case QCOW2_CLUSTER_NORMAL:
|
||||||
|
child = bs->file;
|
||||||
|
copy_offset += offset_into_cluster(s, src_offset);
|
||||||
|
if ((copy_offset & 511) != 0) {
|
||||||
|
ret = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
|
ret = bdrv_co_copy_range_from(child,
|
||||||
|
copy_offset,
|
||||||
|
dst, dst_offset,
|
||||||
|
cur_bytes, cur_flags);
|
||||||
|
qemu_co_mutex_lock(&s->lock);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes -= cur_bytes;
|
||||||
|
src_offset += cur_bytes;
|
||||||
|
dst_offset += cur_bytes;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
out:
|
||||||
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn
|
||||||
|
qcow2_co_copy_range_to(BlockDriverState *bs,
|
||||||
|
BdrvChild *src, uint64_t src_offset,
|
||||||
|
BdrvChild *dst, uint64_t dst_offset,
|
||||||
|
uint64_t bytes, BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
BDRVQcow2State *s = bs->opaque;
|
||||||
|
int offset_in_cluster;
|
||||||
|
int ret;
|
||||||
|
unsigned int cur_bytes; /* number of sectors in current iteration */
|
||||||
|
uint64_t cluster_offset;
|
||||||
|
uint8_t *cluster_data = NULL;
|
||||||
|
QCowL2Meta *l2meta = NULL;
|
||||||
|
|
||||||
|
assert(!bs->encrypted);
|
||||||
|
s->cluster_cache_offset = -1; /* disable compressed cache */
|
||||||
|
|
||||||
|
qemu_co_mutex_lock(&s->lock);
|
||||||
|
|
||||||
|
while (bytes != 0) {
|
||||||
|
|
||||||
|
l2meta = NULL;
|
||||||
|
|
||||||
|
offset_in_cluster = offset_into_cluster(s, dst_offset);
|
||||||
|
cur_bytes = MIN(bytes, INT_MAX);
|
||||||
|
|
||||||
|
/* TODO:
|
||||||
|
* If src->bs == dst->bs, we could simply copy by incrementing
|
||||||
|
* the refcnt, without copying user data.
|
||||||
|
* Or if src->bs == dst->bs->backing->bs, we could copy by discarding. */
|
||||||
|
ret = qcow2_alloc_cluster_offset(bs, dst_offset, &cur_bytes,
|
||||||
|
&cluster_offset, &l2meta);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert((cluster_offset & 511) == 0);
|
||||||
|
|
||||||
|
ret = qcow2_pre_write_overlap_check(bs, 0,
|
||||||
|
cluster_offset + offset_in_cluster, cur_bytes);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
|
ret = bdrv_co_copy_range_to(src, src_offset,
|
||||||
|
bs->file,
|
||||||
|
cluster_offset + offset_in_cluster,
|
||||||
|
cur_bytes, flags);
|
||||||
|
qemu_co_mutex_lock(&s->lock);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qcow2_handle_l2meta(bs, &l2meta, true);
|
||||||
|
if (ret) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes -= cur_bytes;
|
||||||
|
dst_offset += cur_bytes;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
qcow2_handle_l2meta(bs, &l2meta, false);
|
||||||
|
|
||||||
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
|
|
||||||
|
qemu_vfree(cluster_data);
|
||||||
|
trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
|
static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
|
||||||
PreallocMode prealloc, Error **errp)
|
PreallocMode prealloc, Error **errp)
|
||||||
{
|
{
|
||||||
|
@ -4521,6 +4688,8 @@ BlockDriver bdrv_qcow2 = {
|
||||||
|
|
||||||
.bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes,
|
.bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes,
|
||||||
.bdrv_co_pdiscard = qcow2_co_pdiscard,
|
.bdrv_co_pdiscard = qcow2_co_pdiscard,
|
||||||
|
.bdrv_co_copy_range_from = qcow2_co_copy_range_from,
|
||||||
|
.bdrv_co_copy_range_to = qcow2_co_copy_range_to,
|
||||||
.bdrv_truncate = qcow2_truncate,
|
.bdrv_truncate = qcow2_truncate,
|
||||||
.bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed,
|
.bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed,
|
||||||
.bdrv_make_empty = qcow2_make_empty,
|
.bdrv_make_empty = qcow2_make_empty,
|
||||||
|
|
|
@ -167,16 +167,37 @@ static void raw_reopen_abort(BDRVReopenState *state)
|
||||||
state->opaque = NULL;
|
state->opaque = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check and adjust the offset, against 'offset' and 'size' options. */
|
||||||
|
static inline int raw_adjust_offset(BlockDriverState *bs, uint64_t *offset,
|
||||||
|
uint64_t bytes, bool is_write)
|
||||||
|
{
|
||||||
|
BDRVRawState *s = bs->opaque;
|
||||||
|
|
||||||
|
if (s->has_size && (*offset > s->size || bytes > (s->size - *offset))) {
|
||||||
|
/* There's not enough space for the write, or the read request is
|
||||||
|
* out-of-range. Don't read/write anything to prevent leaking out of
|
||||||
|
* the size specified in options. */
|
||||||
|
return is_write ? -ENOSPC : -EINVAL;;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*offset > INT64_MAX - s->offset) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
*offset += s->offset;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset,
|
static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset,
|
||||||
uint64_t bytes, QEMUIOVector *qiov,
|
uint64_t bytes, QEMUIOVector *qiov,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
BDRVRawState *s = bs->opaque;
|
int ret;
|
||||||
|
|
||||||
if (offset > UINT64_MAX - s->offset) {
|
ret = raw_adjust_offset(bs, &offset, bytes, false);
|
||||||
return -EINVAL;
|
if (ret) {
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
offset += s->offset;
|
|
||||||
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
|
||||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
||||||
|
@ -186,23 +207,11 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||||
uint64_t bytes, QEMUIOVector *qiov,
|
uint64_t bytes, QEMUIOVector *qiov,
|
||||||
int flags)
|
int flags)
|
||||||
{
|
{
|
||||||
BDRVRawState *s = bs->opaque;
|
|
||||||
void *buf = NULL;
|
void *buf = NULL;
|
||||||
BlockDriver *drv;
|
BlockDriver *drv;
|
||||||
QEMUIOVector local_qiov;
|
QEMUIOVector local_qiov;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (s->has_size && (offset > s->size || bytes > (s->size - offset))) {
|
|
||||||
/* There's not enough space for the data. Don't write anything and just
|
|
||||||
* fail to prevent leaking out of the size specified in options. */
|
|
||||||
return -ENOSPC;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset > UINT64_MAX - s->offset) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) {
|
if (bs->probed && offset < BLOCK_PROBE_BUF_SIZE && bytes) {
|
||||||
/* Handling partial writes would be a pain - so we just
|
/* Handling partial writes would be a pain - so we just
|
||||||
* require that guests have 512-byte request alignment if
|
* require that guests have 512-byte request alignment if
|
||||||
|
@ -237,7 +246,10 @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
|
||||||
qiov = &local_qiov;
|
qiov = &local_qiov;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += s->offset;
|
ret = raw_adjust_offset(bs, &offset, bytes, true);
|
||||||
|
if (ret) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
|
||||||
ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
ret = bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags);
|
||||||
|
@ -267,22 +279,24 @@ static int coroutine_fn raw_co_pwrite_zeroes(BlockDriverState *bs,
|
||||||
int64_t offset, int bytes,
|
int64_t offset, int bytes,
|
||||||
BdrvRequestFlags flags)
|
BdrvRequestFlags flags)
|
||||||
{
|
{
|
||||||
BDRVRawState *s = bs->opaque;
|
int ret;
|
||||||
if (offset > UINT64_MAX - s->offset) {
|
|
||||||
return -EINVAL;
|
ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
offset += s->offset;
|
|
||||||
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
|
return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs,
|
static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs,
|
||||||
int64_t offset, int bytes)
|
int64_t offset, int bytes)
|
||||||
{
|
{
|
||||||
BDRVRawState *s = bs->opaque;
|
int ret;
|
||||||
if (offset > UINT64_MAX - s->offset) {
|
|
||||||
return -EINVAL;
|
ret = raw_adjust_offset(bs, (uint64_t *)&offset, bytes, true);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
offset += s->offset;
|
|
||||||
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,6 +497,36 @@ static int raw_probe_geometry(BlockDriverState *bs, HDGeometry *geo)
|
||||||
return bdrv_probe_geometry(bs->file->bs, geo);
|
return bdrv_probe_geometry(bs->file->bs, geo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn raw_co_copy_range_from(BlockDriverState *bs,
|
||||||
|
BdrvChild *src, uint64_t src_offset,
|
||||||
|
BdrvChild *dst, uint64_t dst_offset,
|
||||||
|
uint64_t bytes, BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = raw_adjust_offset(bs, &src_offset, bytes, false);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return bdrv_co_copy_range_from(bs->file, src_offset, dst, dst_offset,
|
||||||
|
bytes, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs,
|
||||||
|
BdrvChild *src, uint64_t src_offset,
|
||||||
|
BdrvChild *dst, uint64_t dst_offset,
|
||||||
|
uint64_t bytes, BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = raw_adjust_offset(bs, &dst_offset, bytes, true);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return bdrv_co_copy_range_to(src, src_offset, bs->file, dst_offset, bytes,
|
||||||
|
flags);
|
||||||
|
}
|
||||||
|
|
||||||
BlockDriver bdrv_raw = {
|
BlockDriver bdrv_raw = {
|
||||||
.format_name = "raw",
|
.format_name = "raw",
|
||||||
.instance_size = sizeof(BDRVRawState),
|
.instance_size = sizeof(BDRVRawState),
|
||||||
|
@ -499,6 +543,8 @@ BlockDriver bdrv_raw = {
|
||||||
.bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes,
|
.bdrv_co_pwrite_zeroes = &raw_co_pwrite_zeroes,
|
||||||
.bdrv_co_pdiscard = &raw_co_pdiscard,
|
.bdrv_co_pdiscard = &raw_co_pdiscard,
|
||||||
.bdrv_co_block_status = &raw_co_block_status,
|
.bdrv_co_block_status = &raw_co_block_status,
|
||||||
|
.bdrv_co_copy_range_from = &raw_co_copy_range_from,
|
||||||
|
.bdrv_co_copy_range_to = &raw_co_copy_range_to,
|
||||||
.bdrv_truncate = &raw_truncate,
|
.bdrv_truncate = &raw_truncate,
|
||||||
.bdrv_getlength = &raw_getlength,
|
.bdrv_getlength = &raw_getlength,
|
||||||
.has_variable_length = true,
|
.has_variable_length = true,
|
||||||
|
|
17
configure
vendored
17
configure
vendored
|
@ -5170,6 +5170,20 @@ if test "$fortify_source" != "no"; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
###############################################
|
||||||
|
# Check if copy_file_range is provided by glibc
|
||||||
|
have_copy_file_range=no
|
||||||
|
cat > $TMPC << EOF
|
||||||
|
#include <unistd.h>
|
||||||
|
int main(void) {
|
||||||
|
copy_file_range(0, NULL, 0, NULL, 0, 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
if compile_prog "" "" ; then
|
||||||
|
have_copy_file_range=yes
|
||||||
|
fi
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# check if struct fsxattr is available via linux/fs.h
|
# check if struct fsxattr is available via linux/fs.h
|
||||||
|
|
||||||
|
@ -6273,6 +6287,9 @@ fi
|
||||||
if test "$have_fsxattr" = "yes" ; then
|
if test "$have_fsxattr" = "yes" ; then
|
||||||
echo "HAVE_FSXATTR=y" >> $config_host_mak
|
echo "HAVE_FSXATTR=y" >> $config_host_mak
|
||||||
fi
|
fi
|
||||||
|
if test "$have_copy_file_range" = "yes" ; then
|
||||||
|
echo "HAVE_COPY_FILE_RANGE=y" >> $config_host_mak
|
||||||
|
fi
|
||||||
if test "$vte" = "yes" ; then
|
if test "$vte" = "yes" ; then
|
||||||
echo "CONFIG_VTE=y" >> $config_host_mak
|
echo "CONFIG_VTE=y" >> $config_host_mak
|
||||||
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
|
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
|
||||||
|
|
|
@ -611,4 +611,36 @@ bool bdrv_can_store_new_dirty_bitmap(BlockDriverState *bs, const char *name,
|
||||||
*/
|
*/
|
||||||
void bdrv_register_buf(BlockDriverState *bs, void *host, size_t size);
|
void bdrv_register_buf(BlockDriverState *bs, void *host, size_t size);
|
||||||
void bdrv_unregister_buf(BlockDriverState *bs, void *host);
|
void bdrv_unregister_buf(BlockDriverState *bs, void *host);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* bdrv_co_copy_range:
|
||||||
|
*
|
||||||
|
* Do offloaded copy between two children. If the operation is not implemented
|
||||||
|
* by the driver, or if the backend storage doesn't support it, a negative
|
||||||
|
* error code will be returned.
|
||||||
|
*
|
||||||
|
* Note: block layer doesn't emulate or fallback to a bounce buffer approach
|
||||||
|
* because usually the caller shouldn't attempt offloaded copy any more (e.g.
|
||||||
|
* calling copy_file_range(2)) after the first error, thus it should fall back
|
||||||
|
* to a read+write path in the caller level.
|
||||||
|
*
|
||||||
|
* @src: Source child to copy data from
|
||||||
|
* @src_offset: offset in @src image to read data
|
||||||
|
* @dst: Destination child to copy data to
|
||||||
|
* @dst_offset: offset in @dst image to write data
|
||||||
|
* @bytes: number of bytes to copy
|
||||||
|
* @flags: request flags. Must be one of:
|
||||||
|
* 0 - actually read data from src;
|
||||||
|
* BDRV_REQ_ZERO_WRITE - treat the @src range as zero data and do zero
|
||||||
|
* write on @dst as if bdrv_co_pwrite_zeroes is
|
||||||
|
* called. Used to simplify caller code, or
|
||||||
|
* during BlockDriver.bdrv_co_copy_range_from()
|
||||||
|
* recursion.
|
||||||
|
*
|
||||||
|
* Returns: 0 if succeeded; negative error code if failed.
|
||||||
|
**/
|
||||||
|
int coroutine_fn bdrv_co_copy_range(BdrvChild *src, uint64_t src_offset,
|
||||||
|
BdrvChild *dst, uint64_t dst_offset,
|
||||||
|
uint64_t bytes, BdrvRequestFlags flags);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -204,6 +204,37 @@ struct BlockDriver {
|
||||||
int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs,
|
int coroutine_fn (*bdrv_co_pdiscard)(BlockDriverState *bs,
|
||||||
int64_t offset, int bytes);
|
int64_t offset, int bytes);
|
||||||
|
|
||||||
|
/* Map [offset, offset + nbytes) range onto a child of @bs to copy from,
|
||||||
|
* and invoke bdrv_co_copy_range_from(child, ...), or invoke
|
||||||
|
* bdrv_co_copy_range_to() if @bs is the leaf child to copy data from.
|
||||||
|
*
|
||||||
|
* See the comment of bdrv_co_copy_range for the parameter and return value
|
||||||
|
* semantics.
|
||||||
|
*/
|
||||||
|
int coroutine_fn (*bdrv_co_copy_range_from)(BlockDriverState *bs,
|
||||||
|
BdrvChild *src,
|
||||||
|
uint64_t offset,
|
||||||
|
BdrvChild *dst,
|
||||||
|
uint64_t dst_offset,
|
||||||
|
uint64_t bytes,
|
||||||
|
BdrvRequestFlags flags);
|
||||||
|
|
||||||
|
/* Map [offset, offset + nbytes) range onto a child of bs to copy data to,
|
||||||
|
* and invoke bdrv_co_copy_range_to(child, src, ...), or perform the copy
|
||||||
|
* operation if @bs is the leaf and @src has the same BlockDriver. Return
|
||||||
|
* -ENOTSUP if @bs is the leaf but @src has a different BlockDriver.
|
||||||
|
*
|
||||||
|
* See the comment of bdrv_co_copy_range for the parameter and return value
|
||||||
|
* semantics.
|
||||||
|
*/
|
||||||
|
int coroutine_fn (*bdrv_co_copy_range_to)(BlockDriverState *bs,
|
||||||
|
BdrvChild *src,
|
||||||
|
uint64_t src_offset,
|
||||||
|
BdrvChild *dst,
|
||||||
|
uint64_t dst_offset,
|
||||||
|
uint64_t bytes,
|
||||||
|
BdrvRequestFlags flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Building block for bdrv_block_status[_above] and
|
* Building block for bdrv_block_status[_above] and
|
||||||
* bdrv_is_allocated[_above]. The driver should answer only
|
* bdrv_is_allocated[_above]. The driver should answer only
|
||||||
|
@ -1102,4 +1133,11 @@ void bdrv_dec_in_flight(BlockDriverState *bs);
|
||||||
|
|
||||||
void blockdev_close_all_bdrv_states(void);
|
void blockdev_close_all_bdrv_states(void);
|
||||||
|
|
||||||
|
int coroutine_fn bdrv_co_copy_range_from(BdrvChild *src, uint64_t src_offset,
|
||||||
|
BdrvChild *dst, uint64_t dst_offset,
|
||||||
|
uint64_t bytes, BdrvRequestFlags flags);
|
||||||
|
int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, uint64_t src_offset,
|
||||||
|
BdrvChild *dst, uint64_t dst_offset,
|
||||||
|
uint64_t bytes, BdrvRequestFlags flags);
|
||||||
|
|
||||||
#endif /* BLOCK_INT_H */
|
#endif /* BLOCK_INT_H */
|
||||||
|
|
|
@ -25,9 +25,15 @@
|
||||||
#define QEMU_AIO_FLUSH 0x0008
|
#define QEMU_AIO_FLUSH 0x0008
|
||||||
#define QEMU_AIO_DISCARD 0x0010
|
#define QEMU_AIO_DISCARD 0x0010
|
||||||
#define QEMU_AIO_WRITE_ZEROES 0x0020
|
#define QEMU_AIO_WRITE_ZEROES 0x0020
|
||||||
|
#define QEMU_AIO_COPY_RANGE 0x0040
|
||||||
#define QEMU_AIO_TYPE_MASK \
|
#define QEMU_AIO_TYPE_MASK \
|
||||||
(QEMU_AIO_READ|QEMU_AIO_WRITE|QEMU_AIO_IOCTL|QEMU_AIO_FLUSH| \
|
(QEMU_AIO_READ | \
|
||||||
QEMU_AIO_DISCARD|QEMU_AIO_WRITE_ZEROES)
|
QEMU_AIO_WRITE | \
|
||||||
|
QEMU_AIO_IOCTL | \
|
||||||
|
QEMU_AIO_FLUSH | \
|
||||||
|
QEMU_AIO_DISCARD | \
|
||||||
|
QEMU_AIO_WRITE_ZEROES | \
|
||||||
|
QEMU_AIO_COPY_RANGE)
|
||||||
|
|
||||||
/* AIO flags */
|
/* AIO flags */
|
||||||
#define QEMU_AIO_MISALIGNED 0x1000
|
#define QEMU_AIO_MISALIGNED 0x1000
|
||||||
|
|
|
@ -311,4 +311,8 @@
|
||||||
#define MMC_PROFILE_HDDVD_RW_DL 0x005A
|
#define MMC_PROFILE_HDDVD_RW_DL 0x005A
|
||||||
#define MMC_PROFILE_INVALID 0xFFFF
|
#define MMC_PROFILE_INVALID 0xFFFF
|
||||||
|
|
||||||
|
#define XCOPY_DESC_OFFSET 16
|
||||||
|
#define IDENT_DESCR_TGT_DESCR_SIZE 32
|
||||||
|
#define XCOPY_BLK2BLK_SEG_DESC_SIZE 28
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -232,4 +232,8 @@ void blk_set_force_allow_inactivate(BlockBackend *blk);
|
||||||
void blk_register_buf(BlockBackend *blk, void *host, size_t size);
|
void blk_register_buf(BlockBackend *blk, void *host, size_t size);
|
||||||
void blk_unregister_buf(BlockBackend *blk, void *host);
|
void blk_unregister_buf(BlockBackend *blk, void *host);
|
||||||
|
|
||||||
|
int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in,
|
||||||
|
BlockBackend *blk_out, int64_t off_out,
|
||||||
|
int bytes, BdrvRequestFlags flags);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
50
qemu-img.c
50
qemu-img.c
|
@ -1547,6 +1547,7 @@ typedef struct ImgConvertState {
|
||||||
bool compressed;
|
bool compressed;
|
||||||
bool target_has_backing;
|
bool target_has_backing;
|
||||||
bool wr_in_order;
|
bool wr_in_order;
|
||||||
|
bool copy_range;
|
||||||
int min_sparse;
|
int min_sparse;
|
||||||
size_t cluster_sectors;
|
size_t cluster_sectors;
|
||||||
size_t buf_sectors;
|
size_t buf_sectors;
|
||||||
|
@ -1740,6 +1741,37 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn convert_co_copy_range(ImgConvertState *s, int64_t sector_num,
|
||||||
|
int nb_sectors)
|
||||||
|
{
|
||||||
|
int n, ret;
|
||||||
|
|
||||||
|
while (nb_sectors > 0) {
|
||||||
|
BlockBackend *blk;
|
||||||
|
int src_cur;
|
||||||
|
int64_t bs_sectors, src_cur_offset;
|
||||||
|
int64_t offset;
|
||||||
|
|
||||||
|
convert_select_part(s, sector_num, &src_cur, &src_cur_offset);
|
||||||
|
offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS;
|
||||||
|
blk = s->src[src_cur];
|
||||||
|
bs_sectors = s->src_sectors[src_cur];
|
||||||
|
|
||||||
|
n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset));
|
||||||
|
|
||||||
|
ret = blk_co_copy_range(blk, offset, s->target,
|
||||||
|
sector_num << BDRV_SECTOR_BITS,
|
||||||
|
n << BDRV_SECTOR_BITS, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
sector_num += n;
|
||||||
|
nb_sectors -= n;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void coroutine_fn convert_co_do_copy(void *opaque)
|
static void coroutine_fn convert_co_do_copy(void *opaque)
|
||||||
{
|
{
|
||||||
ImgConvertState *s = opaque;
|
ImgConvertState *s = opaque;
|
||||||
|
@ -1762,6 +1794,7 @@ static void coroutine_fn convert_co_do_copy(void *opaque)
|
||||||
int n;
|
int n;
|
||||||
int64_t sector_num;
|
int64_t sector_num;
|
||||||
enum ImgConvertBlockStatus status;
|
enum ImgConvertBlockStatus status;
|
||||||
|
bool copy_range;
|
||||||
|
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
if (s->ret != -EINPROGRESS || s->sector_num >= s->total_sectors) {
|
if (s->ret != -EINPROGRESS || s->sector_num >= s->total_sectors) {
|
||||||
|
@ -1791,7 +1824,9 @@ static void coroutine_fn convert_co_do_copy(void *opaque)
|
||||||
s->allocated_sectors, 0);
|
s->allocated_sectors, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == BLK_DATA) {
|
retry:
|
||||||
|
copy_range = s->copy_range && s->status == BLK_DATA;
|
||||||
|
if (status == BLK_DATA && !copy_range) {
|
||||||
ret = convert_co_read(s, sector_num, n, buf);
|
ret = convert_co_read(s, sector_num, n, buf);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("error while reading sector %" PRId64
|
error_report("error while reading sector %" PRId64
|
||||||
|
@ -1813,7 +1848,15 @@ static void coroutine_fn convert_co_do_copy(void *opaque)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->ret == -EINPROGRESS) {
|
if (s->ret == -EINPROGRESS) {
|
||||||
ret = convert_co_write(s, sector_num, n, buf, status);
|
if (copy_range) {
|
||||||
|
ret = convert_co_copy_range(s, sector_num, n);
|
||||||
|
if (ret) {
|
||||||
|
s->copy_range = false;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = convert_co_write(s, sector_num, n, buf, status);
|
||||||
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error_report("error while writing sector %" PRId64
|
error_report("error while writing sector %" PRId64
|
||||||
": %s", sector_num, strerror(-ret));
|
": %s", sector_num, strerror(-ret));
|
||||||
|
@ -1936,6 +1979,7 @@ static int img_convert(int argc, char **argv)
|
||||||
ImgConvertState s = (ImgConvertState) {
|
ImgConvertState s = (ImgConvertState) {
|
||||||
/* Need at least 4k of zeros for sparse detection */
|
/* Need at least 4k of zeros for sparse detection */
|
||||||
.min_sparse = 8,
|
.min_sparse = 8,
|
||||||
|
.copy_range = true,
|
||||||
.buf_sectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE,
|
.buf_sectors = IO_BUF_SIZE / BDRV_SECTOR_SIZE,
|
||||||
.wr_in_order = true,
|
.wr_in_order = true,
|
||||||
.num_coroutines = 8,
|
.num_coroutines = 8,
|
||||||
|
@ -1976,6 +2020,7 @@ static int img_convert(int argc, char **argv)
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
s.compressed = true;
|
s.compressed = true;
|
||||||
|
s.copy_range = false;
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
if (!is_valid_option_list(optarg)) {
|
if (!is_valid_option_list(optarg)) {
|
||||||
|
@ -2017,6 +2062,7 @@ static int img_convert(int argc, char **argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.min_sparse = sval / BDRV_SECTOR_SIZE;
|
s.min_sparse = sval / BDRV_SECTOR_SIZE;
|
||||||
|
s.copy_range = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'p':
|
case 'p':
|
||||||
|
|
|
@ -77,7 +77,6 @@ _filter_qemu()
|
||||||
{
|
{
|
||||||
sed -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \
|
sed -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \
|
||||||
-e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#' \
|
-e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#' \
|
||||||
-e '/main-loop: WARNING: I\/O thread spun for [0-9]\+ iterations/d' \
|
|
||||||
-e $'s#\r##' # QEMU monitor uses \r\n line endings
|
-e $'s#\r##' # QEMU monitor uses \r\n line endings
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -222,36 +222,11 @@ static int os_host_main_loop_wait(int64_t timeout)
|
||||||
{
|
{
|
||||||
GMainContext *context = g_main_context_default();
|
GMainContext *context = g_main_context_default();
|
||||||
int ret;
|
int ret;
|
||||||
static int spin_counter;
|
|
||||||
|
|
||||||
g_main_context_acquire(context);
|
g_main_context_acquire(context);
|
||||||
|
|
||||||
glib_pollfds_fill(&timeout);
|
glib_pollfds_fill(&timeout);
|
||||||
|
|
||||||
/* If the I/O thread is very busy or we are incorrectly busy waiting in
|
|
||||||
* the I/O thread, this can lead to starvation of the BQL such that the
|
|
||||||
* VCPU threads never run. To make sure we can detect the later case,
|
|
||||||
* print a message to the screen. If we run into this condition, create
|
|
||||||
* a fake timeout in order to give the VCPU threads a chance to run.
|
|
||||||
*/
|
|
||||||
if (!timeout && (spin_counter > MAX_MAIN_LOOP_SPIN)) {
|
|
||||||
static bool notified;
|
|
||||||
|
|
||||||
if (!notified && !qtest_enabled() && !qtest_driver()) {
|
|
||||||
warn_report("I/O thread spun for %d iterations",
|
|
||||||
MAX_MAIN_LOOP_SPIN);
|
|
||||||
notified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
timeout = SCALE_MS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (timeout) {
|
|
||||||
spin_counter = 0;
|
|
||||||
} else {
|
|
||||||
spin_counter++;
|
|
||||||
}
|
|
||||||
qemu_mutex_unlock_iothread();
|
qemu_mutex_unlock_iothread();
|
||||||
replay_mutex_unlock();
|
replay_mutex_unlock();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue