mirror of https://gitee.com/openkylin/qemu.git
-----BEGIN PGP SIGNATURE-----
iQIcBAABAgAGBQJbOvCTAAoJEL2+eyfA3jBX354P/RFVLsozhcb3DeFj5Ocq2kfS sRt/82Ke/f/w/8lNd4wNbOsdG/eg2M4RmLWF4ONWWoeO7Z0KIatUOTtw5HxjBxBX XfhQy7RZ65luuCHnLDU6c4IdnDvXBVG/kErydhDZjEyeY8qlxrurBB8331cTRFwu hisweIwogPOFDA+/Bty0W0EyVQWFAobL3ExYFlOYFuHwsqJfMPQbytw2zDzC4kjn 8Ecppyt7rfLsEcyf/4OAoHfbbYOiQl7PkXE7/uXDFyL8zPdRpIlDFSZtmy1Zb213 mcYhPmehUkFHV/BDF/LdnzjlraK8oMaNu0IDld5cX/1xUU4VtbW2YjAt6OdCn7Ll 7YbNNKYU/mM1QUPshX4qJkbUaCu7JoTDKPiBbJei/MV7zMJBLpNVG/AuJE2gbweI 2levV76QzS2+fQVKv/9LUliqOYEp5T0/aybb+35Vzhf5WNpSO7s1oaCDAvSgUhS+ qU1MIAROQQPCmdM8PwqzG9b2TGp/tYcWOju5bqt488Twmo0BbTGjYCFl6StJHibC mN5fASP5nQiz1fc3FrBp0h/PCQlGtd2ZgeyeC+lkPVcFovclA2vo/ib1k2LK/0nU TzkKIFRJZH58yYjppuBrB6c/aFfVkutE4Hz25i+3nZ91ZEyQKbv1mDxCyRNXNWjt Gteul6gUo/AjzMOWFFvH =AKR7 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/cody/tags/block-pull-request' into staging # gpg: Signature made Tue 03 Jul 2018 04:42:11 BST # gpg: using RSA key BDBE7B27C0DE3057 # gpg: Good signature from "Jeffrey Cody <jcody@redhat.com>" # gpg: aka "Jeffrey Cody <jeff@codyprime.org>" # gpg: aka "Jeffrey Cody <codyprime@gmail.com>" # Primary key fingerprint: 9957 4B4D 3474 90E7 9D98 D624 BDBE 7B27 C0DE 3057 * remotes/cody/tags/block-pull-request: backup: Use copy offloading block: Honour BDRV_REQ_NO_SERIALISING in copy range block: Fix parameter checking in bdrv_co_copy_range_internal Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
a395717cbd
150
block/backup.c
150
block/backup.c
|
@ -45,6 +45,8 @@ typedef struct BackupBlockJob {
|
||||||
QLIST_HEAD(, CowRequest) inflight_reqs;
|
QLIST_HEAD(, CowRequest) inflight_reqs;
|
||||||
|
|
||||||
HBitmap *copy_bitmap;
|
HBitmap *copy_bitmap;
|
||||||
|
bool use_copy_range;
|
||||||
|
int64_t copy_range_size;
|
||||||
} BackupBlockJob;
|
} BackupBlockJob;
|
||||||
|
|
||||||
static const BlockJobDriver backup_job_driver;
|
static const BlockJobDriver backup_job_driver;
|
||||||
|
@ -86,19 +88,101 @@ static void cow_request_end(CowRequest *req)
|
||||||
qemu_co_queue_restart_all(&req->wait_queue);
|
qemu_co_queue_restart_all(&req->wait_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Copy range to target with a bounce buffer and return the bytes copied. If
|
||||||
|
* error occured, return a negative error number */
|
||||||
|
static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job,
|
||||||
|
int64_t start,
|
||||||
|
int64_t end,
|
||||||
|
bool is_write_notifier,
|
||||||
|
bool *error_is_read,
|
||||||
|
void **bounce_buffer)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct iovec iov;
|
||||||
|
QEMUIOVector qiov;
|
||||||
|
BlockBackend *blk = job->common.blk;
|
||||||
|
int nbytes;
|
||||||
|
|
||||||
|
hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
|
||||||
|
nbytes = MIN(job->cluster_size, job->len - start);
|
||||||
|
if (!*bounce_buffer) {
|
||||||
|
*bounce_buffer = blk_blockalign(blk, job->cluster_size);
|
||||||
|
}
|
||||||
|
iov.iov_base = *bounce_buffer;
|
||||||
|
iov.iov_len = nbytes;
|
||||||
|
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||||
|
|
||||||
|
ret = blk_co_preadv(blk, start, qiov.size, &qiov,
|
||||||
|
is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
trace_backup_do_cow_read_fail(job, start, ret);
|
||||||
|
if (error_is_read) {
|
||||||
|
*error_is_read = true;
|
||||||
|
}
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (qemu_iovec_is_zero(&qiov)) {
|
||||||
|
ret = blk_co_pwrite_zeroes(job->target, start,
|
||||||
|
qiov.size, BDRV_REQ_MAY_UNMAP);
|
||||||
|
} else {
|
||||||
|
ret = blk_co_pwritev(job->target, start,
|
||||||
|
qiov.size, &qiov,
|
||||||
|
job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
trace_backup_do_cow_write_fail(job, start, ret);
|
||||||
|
if (error_is_read) {
|
||||||
|
*error_is_read = false;
|
||||||
|
}
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nbytes;
|
||||||
|
fail:
|
||||||
|
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy range to target and return the bytes copied. If error occured, return a
|
||||||
|
* negative error number. */
|
||||||
|
static int coroutine_fn backup_cow_with_offload(BackupBlockJob *job,
|
||||||
|
int64_t start,
|
||||||
|
int64_t end,
|
||||||
|
bool is_write_notifier)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int nr_clusters;
|
||||||
|
BlockBackend *blk = job->common.blk;
|
||||||
|
int nbytes;
|
||||||
|
|
||||||
|
assert(QEMU_IS_ALIGNED(job->copy_range_size, job->cluster_size));
|
||||||
|
nbytes = MIN(job->copy_range_size, end - start);
|
||||||
|
nr_clusters = DIV_ROUND_UP(nbytes, job->cluster_size);
|
||||||
|
hbitmap_reset(job->copy_bitmap, start / job->cluster_size,
|
||||||
|
nr_clusters);
|
||||||
|
ret = blk_co_copy_range(blk, start, job->target, start, nbytes,
|
||||||
|
is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
trace_backup_do_cow_copy_range_fail(job, start, ret);
|
||||||
|
hbitmap_set(job->copy_bitmap, start / job->cluster_size,
|
||||||
|
nr_clusters);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nbytes;
|
||||||
|
}
|
||||||
|
|
||||||
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||||
int64_t offset, uint64_t bytes,
|
int64_t offset, uint64_t bytes,
|
||||||
bool *error_is_read,
|
bool *error_is_read,
|
||||||
bool is_write_notifier)
|
bool is_write_notifier)
|
||||||
{
|
{
|
||||||
BlockBackend *blk = job->common.blk;
|
|
||||||
CowRequest cow_request;
|
CowRequest cow_request;
|
||||||
struct iovec iov;
|
|
||||||
QEMUIOVector bounce_qiov;
|
|
||||||
void *bounce_buffer = NULL;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int64_t start, end; /* bytes */
|
int64_t start, end; /* bytes */
|
||||||
int n; /* bytes */
|
void *bounce_buffer = NULL;
|
||||||
|
|
||||||
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
||||||
|
|
||||||
|
@ -110,60 +194,38 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||||
wait_for_overlapping_requests(job, start, end);
|
wait_for_overlapping_requests(job, start, end);
|
||||||
cow_request_begin(&cow_request, job, start, end);
|
cow_request_begin(&cow_request, job, start, end);
|
||||||
|
|
||||||
for (; start < end; start += job->cluster_size) {
|
while (start < end) {
|
||||||
if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) {
|
if (!hbitmap_get(job->copy_bitmap, start / job->cluster_size)) {
|
||||||
trace_backup_do_cow_skip(job, start);
|
trace_backup_do_cow_skip(job, start);
|
||||||
|
start += job->cluster_size;
|
||||||
continue; /* already copied */
|
continue; /* already copied */
|
||||||
}
|
}
|
||||||
hbitmap_reset(job->copy_bitmap, start / job->cluster_size, 1);
|
|
||||||
|
|
||||||
trace_backup_do_cow_process(job, start);
|
trace_backup_do_cow_process(job, start);
|
||||||
|
|
||||||
n = MIN(job->cluster_size, job->len - start);
|
if (job->use_copy_range) {
|
||||||
|
ret = backup_cow_with_offload(job, start, end, is_write_notifier);
|
||||||
if (!bounce_buffer) {
|
if (ret < 0) {
|
||||||
bounce_buffer = blk_blockalign(blk, job->cluster_size);
|
job->use_copy_range = false;
|
||||||
}
|
|
||||||
iov.iov_base = bounce_buffer;
|
|
||||||
iov.iov_len = n;
|
|
||||||
qemu_iovec_init_external(&bounce_qiov, &iov, 1);
|
|
||||||
|
|
||||||
ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov,
|
|
||||||
is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
|
|
||||||
if (ret < 0) {
|
|
||||||
trace_backup_do_cow_read_fail(job, start, ret);
|
|
||||||
if (error_is_read) {
|
|
||||||
*error_is_read = true;
|
|
||||||
}
|
}
|
||||||
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
if (!job->use_copy_range) {
|
||||||
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
ret = backup_cow_with_bounce_buffer(job, start, end, is_write_notifier,
|
||||||
ret = blk_co_pwrite_zeroes(job->target, start,
|
error_is_read, &bounce_buffer);
|
||||||
bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
|
|
||||||
} else {
|
|
||||||
ret = blk_co_pwritev(job->target, start,
|
|
||||||
bounce_qiov.size, &bounce_qiov,
|
|
||||||
job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
|
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
trace_backup_do_cow_write_fail(job, start, ret);
|
break;
|
||||||
if (error_is_read) {
|
|
||||||
*error_is_read = false;
|
|
||||||
}
|
|
||||||
hbitmap_set(job->copy_bitmap, start / job->cluster_size, 1);
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Publish progress, guest I/O counts as progress too. Note that the
|
/* Publish progress, guest I/O counts as progress too. Note that the
|
||||||
* offset field is an opaque progress value, it is not a disk offset.
|
* offset field is an opaque progress value, it is not a disk offset.
|
||||||
*/
|
*/
|
||||||
job->bytes_read += n;
|
start += ret;
|
||||||
job_progress_update(&job->common.job, n);
|
job->bytes_read += ret;
|
||||||
|
job_progress_update(&job->common.job, ret);
|
||||||
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
|
||||||
if (bounce_buffer) {
|
if (bounce_buffer) {
|
||||||
qemu_vfree(bounce_buffer);
|
qemu_vfree(bounce_buffer);
|
||||||
}
|
}
|
||||||
|
@ -665,6 +727,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
|
||||||
} else {
|
} else {
|
||||||
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
|
job->cluster_size = MAX(BACKUP_CLUSTER_SIZE_DEFAULT, bdi.cluster_size);
|
||||||
}
|
}
|
||||||
|
job->use_copy_range = true;
|
||||||
|
job->copy_range_size = MIN_NON_ZERO(blk_get_max_transfer(job->common.blk),
|
||||||
|
blk_get_max_transfer(job->target));
|
||||||
|
job->copy_range_size = MAX(job->cluster_size,
|
||||||
|
QEMU_ALIGN_UP(job->copy_range_size,
|
||||||
|
job->cluster_size));
|
||||||
|
|
||||||
/* Required permissions are already taken with target's blk_new() */
|
/* Required permissions are already taken with target's blk_new() */
|
||||||
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
|
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
|
||||||
|
|
35
block/io.c
35
block/io.c
|
@ -2897,18 +2897,11 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src,
|
||||||
bool recurse_src)
|
bool recurse_src)
|
||||||
{
|
{
|
||||||
BdrvTrackedRequest src_req, dst_req;
|
BdrvTrackedRequest src_req, dst_req;
|
||||||
BlockDriverState *src_bs = src->bs;
|
|
||||||
BlockDriverState *dst_bs = dst->bs;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!src || !dst || !src->bs || !dst->bs) {
|
if (!dst || !dst->bs) {
|
||||||
return -ENOMEDIUM;
|
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);
|
ret = bdrv_check_byte_request(dst->bs, dst_offset, bytes);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2917,20 +2910,30 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src,
|
||||||
return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, flags);
|
return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!src || !src->bs) {
|
||||||
|
return -ENOMEDIUM;
|
||||||
|
}
|
||||||
|
ret = bdrv_check_byte_request(src->bs, src_offset, bytes);
|
||||||
|
if (ret) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (!src->bs->drv->bdrv_co_copy_range_from
|
if (!src->bs->drv->bdrv_co_copy_range_from
|
||||||
|| !dst->bs->drv->bdrv_co_copy_range_to
|
|| !dst->bs->drv->bdrv_co_copy_range_to
|
||||||
|| src->bs->encrypted || dst->bs->encrypted) {
|
|| src->bs->encrypted || dst->bs->encrypted) {
|
||||||
return -ENOTSUP;
|
return -ENOTSUP;
|
||||||
}
|
}
|
||||||
bdrv_inc_in_flight(src_bs);
|
bdrv_inc_in_flight(src->bs);
|
||||||
bdrv_inc_in_flight(dst_bs);
|
bdrv_inc_in_flight(dst->bs);
|
||||||
tracked_request_begin(&src_req, src_bs, src_offset,
|
tracked_request_begin(&src_req, src->bs, src_offset,
|
||||||
bytes, BDRV_TRACKED_READ);
|
bytes, BDRV_TRACKED_READ);
|
||||||
tracked_request_begin(&dst_req, dst_bs, dst_offset,
|
tracked_request_begin(&dst_req, dst->bs, dst_offset,
|
||||||
bytes, BDRV_TRACKED_WRITE);
|
bytes, BDRV_TRACKED_WRITE);
|
||||||
|
|
||||||
wait_serialising_requests(&src_req);
|
if (!(flags & BDRV_REQ_NO_SERIALISING)) {
|
||||||
wait_serialising_requests(&dst_req);
|
wait_serialising_requests(&src_req);
|
||||||
|
wait_serialising_requests(&dst_req);
|
||||||
|
}
|
||||||
if (recurse_src) {
|
if (recurse_src) {
|
||||||
ret = src->bs->drv->bdrv_co_copy_range_from(src->bs,
|
ret = src->bs->drv->bdrv_co_copy_range_from(src->bs,
|
||||||
src, src_offset,
|
src, src_offset,
|
||||||
|
@ -2944,8 +2947,8 @@ static int coroutine_fn bdrv_co_copy_range_internal(BdrvChild *src,
|
||||||
}
|
}
|
||||||
tracked_request_end(&src_req);
|
tracked_request_end(&src_req);
|
||||||
tracked_request_end(&dst_req);
|
tracked_request_end(&dst_req);
|
||||||
bdrv_dec_in_flight(src_bs);
|
bdrv_dec_in_flight(src->bs);
|
||||||
bdrv_dec_in_flight(dst_bs);
|
bdrv_dec_in_flight(dst->bs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64
|
||||||
backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64
|
backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64
|
||||||
backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
||||||
backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
||||||
|
backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
||||||
|
|
||||||
# blockdev.c
|
# blockdev.c
|
||||||
qmp_block_job_cancel(void *job) "job %p"
|
qmp_block_job_cancel(void *job) "job %p"
|
||||||
|
|
|
@ -659,13 +659,14 @@ void bdrv_unregister_buf(BlockDriverState *bs, void *host);
|
||||||
* @dst: Destination child to copy data to
|
* @dst: Destination child to copy data to
|
||||||
* @dst_offset: offset in @dst image to write data
|
* @dst_offset: offset in @dst image to write data
|
||||||
* @bytes: number of bytes to copy
|
* @bytes: number of bytes to copy
|
||||||
* @flags: request flags. Must be one of:
|
* @flags: request flags. Supported flags:
|
||||||
* 0 - actually read data from src;
|
|
||||||
* BDRV_REQ_ZERO_WRITE - treat the @src range as zero data and do zero
|
* BDRV_REQ_ZERO_WRITE - treat the @src range as zero data and do zero
|
||||||
* write on @dst as if bdrv_co_pwrite_zeroes is
|
* write on @dst as if bdrv_co_pwrite_zeroes is
|
||||||
* called. Used to simplify caller code, or
|
* called. Used to simplify caller code, or
|
||||||
* during BlockDriver.bdrv_co_copy_range_from()
|
* during BlockDriver.bdrv_co_copy_range_from()
|
||||||
* recursion.
|
* recursion.
|
||||||
|
* BDRV_REQ_NO_SERIALISING - do not serialize with other overlapping
|
||||||
|
* requests currently in flight.
|
||||||
*
|
*
|
||||||
* Returns: 0 if succeeded; negative error code if failed.
|
* Returns: 0 if succeeded; negative error code if failed.
|
||||||
**/
|
**/
|
||||||
|
|
Loading…
Reference in New Issue