block/backup: make backup cluster size configurable
64K might not always be appropriate, make this a runtime value. Signed-off-by: John Snow <jsnow@redhat.com> Reviewed-by: Fam Zheng <famz@redhat.com> Message-id: 1456433911-24718-2-git-send-email-jsnow@redhat.com Signed-off-by: Jeff Cody <jcody@redhat.com>
This commit is contained in:
parent
21cd917ff5
commit
16096a4d47
@ -21,10 +21,7 @@
|
|||||||
#include "qemu/ratelimit.h"
|
#include "qemu/ratelimit.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
|
|
||||||
#define BACKUP_CLUSTER_BITS 16
|
#define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16)
|
||||||
#define BACKUP_CLUSTER_SIZE (1 << BACKUP_CLUSTER_BITS)
|
|
||||||
#define BACKUP_SECTORS_PER_CLUSTER (BACKUP_CLUSTER_SIZE / BDRV_SECTOR_SIZE)
|
|
||||||
|
|
||||||
#define SLICE_TIME 100000000ULL /* ns */
|
#define SLICE_TIME 100000000ULL /* ns */
|
||||||
|
|
||||||
typedef struct CowRequest {
|
typedef struct CowRequest {
|
||||||
@ -46,9 +43,16 @@ typedef struct BackupBlockJob {
|
|||||||
CoRwlock flush_rwlock;
|
CoRwlock flush_rwlock;
|
||||||
uint64_t sectors_read;
|
uint64_t sectors_read;
|
||||||
HBitmap *bitmap;
|
HBitmap *bitmap;
|
||||||
|
int64_t cluster_size;
|
||||||
QLIST_HEAD(, CowRequest) inflight_reqs;
|
QLIST_HEAD(, CowRequest) inflight_reqs;
|
||||||
} BackupBlockJob;
|
} BackupBlockJob;
|
||||||
|
|
||||||
|
/* Size of a cluster in sectors, instead of bytes. */
|
||||||
|
static inline int64_t cluster_size_sectors(BackupBlockJob *job)
|
||||||
|
{
|
||||||
|
return job->cluster_size / BDRV_SECTOR_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
/* See if in-flight requests overlap and wait for them to complete */
|
/* See if in-flight requests overlap and wait for them to complete */
|
||||||
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
||||||
int64_t start,
|
int64_t start,
|
||||||
@ -97,13 +101,14 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
QEMUIOVector bounce_qiov;
|
QEMUIOVector bounce_qiov;
|
||||||
void *bounce_buffer = NULL;
|
void *bounce_buffer = NULL;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||||
int64_t start, end;
|
int64_t start, end;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
||||||
|
|
||||||
start = sector_num / BACKUP_SECTORS_PER_CLUSTER;
|
start = sector_num / sectors_per_cluster;
|
||||||
end = DIV_ROUND_UP(sector_num + nb_sectors, BACKUP_SECTORS_PER_CLUSTER);
|
end = DIV_ROUND_UP(sector_num + nb_sectors, sectors_per_cluster);
|
||||||
|
|
||||||
trace_backup_do_cow_enter(job, start, sector_num, nb_sectors);
|
trace_backup_do_cow_enter(job, start, sector_num, nb_sectors);
|
||||||
|
|
||||||
@ -118,12 +123,12 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
|
|
||||||
trace_backup_do_cow_process(job, start);
|
trace_backup_do_cow_process(job, start);
|
||||||
|
|
||||||
n = MIN(BACKUP_SECTORS_PER_CLUSTER,
|
n = MIN(sectors_per_cluster,
|
||||||
job->common.len / BDRV_SECTOR_SIZE -
|
job->common.len / BDRV_SECTOR_SIZE -
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER);
|
start * sectors_per_cluster);
|
||||||
|
|
||||||
if (!bounce_buffer) {
|
if (!bounce_buffer) {
|
||||||
bounce_buffer = qemu_blockalign(bs, BACKUP_CLUSTER_SIZE);
|
bounce_buffer = qemu_blockalign(bs, job->cluster_size);
|
||||||
}
|
}
|
||||||
iov.iov_base = bounce_buffer;
|
iov.iov_base = bounce_buffer;
|
||||||
iov.iov_len = n * BDRV_SECTOR_SIZE;
|
iov.iov_len = n * BDRV_SECTOR_SIZE;
|
||||||
@ -131,10 +136,10 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
|
|
||||||
if (is_write_notifier) {
|
if (is_write_notifier) {
|
||||||
ret = bdrv_co_readv_no_serialising(bs,
|
ret = bdrv_co_readv_no_serialising(bs,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER,
|
start * sectors_per_cluster,
|
||||||
n, &bounce_qiov);
|
n, &bounce_qiov);
|
||||||
} else {
|
} else {
|
||||||
ret = bdrv_co_readv(bs, start * BACKUP_SECTORS_PER_CLUSTER, n,
|
ret = bdrv_co_readv(bs, start * sectors_per_cluster, n,
|
||||||
&bounce_qiov);
|
&bounce_qiov);
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -147,11 +152,11 @@ static int coroutine_fn backup_do_cow(BlockDriverState *bs,
|
|||||||
|
|
||||||
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
||||||
ret = bdrv_co_write_zeroes(job->target,
|
ret = bdrv_co_write_zeroes(job->target,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER,
|
start * sectors_per_cluster,
|
||||||
n, BDRV_REQ_MAY_UNMAP);
|
n, BDRV_REQ_MAY_UNMAP);
|
||||||
} else {
|
} else {
|
||||||
ret = bdrv_co_writev(job->target,
|
ret = bdrv_co_writev(job->target,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER, n,
|
start * sectors_per_cluster, n,
|
||||||
&bounce_qiov);
|
&bounce_qiov);
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -322,21 +327,22 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||||||
int64_t cluster;
|
int64_t cluster;
|
||||||
int64_t end;
|
int64_t end;
|
||||||
int64_t last_cluster = -1;
|
int64_t last_cluster = -1;
|
||||||
|
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||||
BlockDriverState *bs = job->common.bs;
|
BlockDriverState *bs = job->common.bs;
|
||||||
HBitmapIter hbi;
|
HBitmapIter hbi;
|
||||||
|
|
||||||
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
||||||
clusters_per_iter = MAX((granularity / BACKUP_CLUSTER_SIZE), 1);
|
clusters_per_iter = MAX((granularity / job->cluster_size), 1);
|
||||||
bdrv_dirty_iter_init(job->sync_bitmap, &hbi);
|
bdrv_dirty_iter_init(job->sync_bitmap, &hbi);
|
||||||
|
|
||||||
/* Find the next dirty sector(s) */
|
/* Find the next dirty sector(s) */
|
||||||
while ((sector = hbitmap_iter_next(&hbi)) != -1) {
|
while ((sector = hbitmap_iter_next(&hbi)) != -1) {
|
||||||
cluster = sector / BACKUP_SECTORS_PER_CLUSTER;
|
cluster = sector / sectors_per_cluster;
|
||||||
|
|
||||||
/* Fake progress updates for any clusters we skipped */
|
/* Fake progress updates for any clusters we skipped */
|
||||||
if (cluster != last_cluster + 1) {
|
if (cluster != last_cluster + 1) {
|
||||||
job->common.offset += ((cluster - last_cluster - 1) *
|
job->common.offset += ((cluster - last_cluster - 1) *
|
||||||
BACKUP_CLUSTER_SIZE);
|
job->cluster_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
|
for (end = cluster + clusters_per_iter; cluster < end; cluster++) {
|
||||||
@ -344,8 +350,8 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||||||
if (yield_and_check(job)) {
|
if (yield_and_check(job)) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
ret = backup_do_cow(bs, cluster * BACKUP_SECTORS_PER_CLUSTER,
|
ret = backup_do_cow(bs, cluster * sectors_per_cluster,
|
||||||
BACKUP_SECTORS_PER_CLUSTER, &error_is_read,
|
sectors_per_cluster, &error_is_read,
|
||||||
false);
|
false);
|
||||||
if ((ret < 0) &&
|
if ((ret < 0) &&
|
||||||
backup_error_action(job, error_is_read, -ret) ==
|
backup_error_action(job, error_is_read, -ret) ==
|
||||||
@ -357,17 +363,17 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||||||
|
|
||||||
/* If the bitmap granularity is smaller than the backup granularity,
|
/* If the bitmap granularity is smaller than the backup granularity,
|
||||||
* we need to advance the iterator pointer to the next cluster. */
|
* we need to advance the iterator pointer to the next cluster. */
|
||||||
if (granularity < BACKUP_CLUSTER_SIZE) {
|
if (granularity < job->cluster_size) {
|
||||||
bdrv_set_dirty_iter(&hbi, cluster * BACKUP_SECTORS_PER_CLUSTER);
|
bdrv_set_dirty_iter(&hbi, cluster * sectors_per_cluster);
|
||||||
}
|
}
|
||||||
|
|
||||||
last_cluster = cluster - 1;
|
last_cluster = cluster - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Play some final catchup with the progress meter */
|
/* Play some final catchup with the progress meter */
|
||||||
end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE);
|
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||||
if (last_cluster + 1 < end) {
|
if (last_cluster + 1 < end) {
|
||||||
job->common.offset += ((end - last_cluster - 1) * BACKUP_CLUSTER_SIZE);
|
job->common.offset += ((end - last_cluster - 1) * job->cluster_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -384,13 +390,14 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
.notify = backup_before_write_notify,
|
.notify = backup_before_write_notify,
|
||||||
};
|
};
|
||||||
int64_t start, end;
|
int64_t start, end;
|
||||||
|
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
QLIST_INIT(&job->inflight_reqs);
|
QLIST_INIT(&job->inflight_reqs);
|
||||||
qemu_co_rwlock_init(&job->flush_rwlock);
|
qemu_co_rwlock_init(&job->flush_rwlock);
|
||||||
|
|
||||||
start = 0;
|
start = 0;
|
||||||
end = DIV_ROUND_UP(job->common.len, BACKUP_CLUSTER_SIZE);
|
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||||
|
|
||||||
job->bitmap = hbitmap_alloc(end, 0);
|
job->bitmap = hbitmap_alloc(end, 0);
|
||||||
|
|
||||||
@ -427,7 +434,7 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
/* Check to see if these blocks are already in the
|
/* Check to see if these blocks are already in the
|
||||||
* backing file. */
|
* backing file. */
|
||||||
|
|
||||||
for (i = 0; i < BACKUP_SECTORS_PER_CLUSTER;) {
|
for (i = 0; i < sectors_per_cluster;) {
|
||||||
/* bdrv_is_allocated() only returns true/false based
|
/* bdrv_is_allocated() only returns true/false based
|
||||||
* on the first set of sectors it comes across that
|
* on the first set of sectors it comes across that
|
||||||
* are are all in the same state.
|
* are are all in the same state.
|
||||||
@ -436,8 +443,8 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
* needed but at some point that is always the case. */
|
* needed but at some point that is always the case. */
|
||||||
alloced =
|
alloced =
|
||||||
bdrv_is_allocated(bs,
|
bdrv_is_allocated(bs,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER + i,
|
start * sectors_per_cluster + i,
|
||||||
BACKUP_SECTORS_PER_CLUSTER - i, &n);
|
sectors_per_cluster - i, &n);
|
||||||
i += n;
|
i += n;
|
||||||
|
|
||||||
if (alloced == 1 || n == 0) {
|
if (alloced == 1 || n == 0) {
|
||||||
@ -452,8 +459,8 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* FULL sync mode we copy the whole drive. */
|
/* FULL sync mode we copy the whole drive. */
|
||||||
ret = backup_do_cow(bs, start * BACKUP_SECTORS_PER_CLUSTER,
|
ret = backup_do_cow(bs, start * sectors_per_cluster,
|
||||||
BACKUP_SECTORS_PER_CLUSTER, &error_is_read, false);
|
sectors_per_cluster, &error_is_read, false);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
/* Depending on error action, fail now or retry cluster */
|
/* Depending on error action, fail now or retry cluster */
|
||||||
BlockErrorAction action =
|
BlockErrorAction action =
|
||||||
@ -571,6 +578,7 @@ void backup_start(BlockDriverState *bs, BlockDriverState *target,
|
|||||||
job->sync_mode = sync_mode;
|
job->sync_mode = sync_mode;
|
||||||
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
|
job->sync_bitmap = sync_mode == MIRROR_SYNC_MODE_INCREMENTAL ?
|
||||||
sync_bitmap : NULL;
|
sync_bitmap : NULL;
|
||||||
|
job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT;
|
||||||
job->common.len = len;
|
job->common.len = len;
|
||||||
job->common.co = qemu_coroutine_create(backup_run);
|
job->common.co = qemu_coroutine_create(backup_run);
|
||||||
block_job_txn_add_job(txn, &job->common);
|
block_job_txn_add_job(txn, &job->common);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user