mirror of https://github.com/xemu-project/xemu.git
Block layer patches
-----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iQIcBAABAgAGBQJZY2R0AAoJEH8JsnLIjy/WFNcQALUMh6O/eaXzHrAER3xAA3oF WghkekAbVa6TiGoH90UEOrdBS64I7HuWYtwVQQ/9FVV/3Hc1Z15VnBUMrsvcwhvw xwIfMilj/av6bk/DoG1S5wb8T+6yUPBpuBzQhsiGnrnI0o9wMzWpSZn41alxCqpe jVMpOvDbLIDB9foQhyNAFUGBjhvkBIAe8O8yIkninZB07r87gUN78388NTybLLNh I15R9kWKJlqQxWrX4OT7ShcPnuTJClQyjfu1P4BAHzXpv8ZicBYZ/ZR6Q2verZpu kBwXlBxnWKJDOPyd1sKBNe3Mo3KqKXfSL0NOg00PHPRTcgh+e+QYZ4ZxG0Dv6Cyl rb6Cy0FBuM6JtjhuCLt9yZ1eErCtpHJq901T7DMb4LQ3IuFrDJToojdPTaOx4st4 lh7tiraQtp3OKzh/H7smZ5WT7V8Sg2l3mPAs3r7iPihzceS9yPUbka3yB2xbwnpn e7H8IzwHFgqRprR+Ii8Z+0eWHApOJDSK2mCcR1OGJEpxXz2gYn6+WrvKBKxDScln deIo7Kz6/5OehRKjPGWJmYsgpQBzlLoPUXk1K75OMOu2y4sLGaFr/ZqZgLjOxunZ jJ2h5TGgzkRZOsIKQAhrBkBZ09MOI7PMyxkbGu4o+qbovG3InE9kHzH2HBsIIDKS zhG+zHFGha8YsyKS6a6Q =+05n -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging Block layer patches # gpg: Signature made Mon 10 Jul 2017 12:26:44 BST # gpg: using RSA key 0x7F09B272C88F2FD6 # gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>" # Primary key fingerprint: DC3D EB15 9A9A F95D 3D74 56FE 7F09 B272 C88F 2FD6 * remotes/kevin/tags/for-upstream: (40 commits) block: Make bdrv_is_allocated_above() byte-based block: Minimize raw use of bds->total_sectors block: Make bdrv_is_allocated() byte-based backup: Switch backup_run() to byte-based backup: Switch backup_do_cow() to byte-based backup: Switch block_backup.h to byte-based backup: Switch BackupBlockJob to byte-based block: Drop unused bdrv_round_sectors_to_clusters() mirror: Switch mirror_iteration() to byte-based mirror: Switch mirror_do_read() to byte-based mirror: Switch mirror_cow_align() to byte-based mirror: Update signature of mirror_clip_sectors() mirror: Switch mirror_do_zero_or_discard() to byte-based mirror: Switch MirrorBlockJob to byte-based commit: Switch commit_run() to byte-based commit: Switch commit_populate() to byte-based stream: Switch stream_run() to byte-based stream: Drop reached_end for stream_complete() stream: Switch stream_populate() to byte-based trace: Show blockjob actions via bytes, not sectors ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
94c56652b9
128
block/backup.c
128
block/backup.c
|
@ -39,7 +39,7 @@ typedef struct BackupBlockJob {
|
|||
BlockdevOnError on_source_error;
|
||||
BlockdevOnError on_target_error;
|
||||
CoRwlock flush_rwlock;
|
||||
uint64_t sectors_read;
|
||||
uint64_t bytes_read;
|
||||
unsigned long *done_bitmap;
|
||||
int64_t cluster_size;
|
||||
bool compress;
|
||||
|
@ -47,12 +47,6 @@ typedef struct BackupBlockJob {
|
|||
QLIST_HEAD(, CowRequest) inflight_reqs;
|
||||
} 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 */
|
||||
static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
||||
int64_t start,
|
||||
|
@ -64,7 +58,7 @@ static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
|||
do {
|
||||
retry = false;
|
||||
QLIST_FOREACH(req, &job->inflight_reqs, list) {
|
||||
if (end > req->start && start < req->end) {
|
||||
if (end > req->start_byte && start < req->end_byte) {
|
||||
qemu_co_queue_wait(&req->wait_queue, NULL);
|
||||
retry = true;
|
||||
break;
|
||||
|
@ -75,10 +69,10 @@ static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
|
|||
|
||||
/* Keep track of an in-flight request */
|
||||
static void cow_request_begin(CowRequest *req, BackupBlockJob *job,
|
||||
int64_t start, int64_t end)
|
||||
int64_t start, int64_t end)
|
||||
{
|
||||
req->start = start;
|
||||
req->end = end;
|
||||
req->start_byte = start;
|
||||
req->end_byte = end;
|
||||
qemu_co_queue_init(&req->wait_queue);
|
||||
QLIST_INSERT_HEAD(&job->inflight_reqs, req, list);
|
||||
}
|
||||
|
@ -91,7 +85,7 @@ static void cow_request_end(CowRequest *req)
|
|||
}
|
||||
|
||||
static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
int64_t offset, uint64_t bytes,
|
||||
bool *error_is_read,
|
||||
bool is_write_notifier)
|
||||
{
|
||||
|
@ -101,41 +95,37 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
|||
QEMUIOVector bounce_qiov;
|
||||
void *bounce_buffer = NULL;
|
||||
int ret = 0;
|
||||
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||
int64_t start, end;
|
||||
int n;
|
||||
int64_t start, end; /* bytes */
|
||||
int n; /* bytes */
|
||||
|
||||
qemu_co_rwlock_rdlock(&job->flush_rwlock);
|
||||
|
||||
start = sector_num / sectors_per_cluster;
|
||||
end = DIV_ROUND_UP(sector_num + nb_sectors, sectors_per_cluster);
|
||||
start = QEMU_ALIGN_DOWN(offset, job->cluster_size);
|
||||
end = QEMU_ALIGN_UP(bytes + offset, job->cluster_size);
|
||||
|
||||
trace_backup_do_cow_enter(job, start, sector_num, nb_sectors);
|
||||
trace_backup_do_cow_enter(job, start, offset, bytes);
|
||||
|
||||
wait_for_overlapping_requests(job, start, end);
|
||||
cow_request_begin(&cow_request, job, start, end);
|
||||
|
||||
for (; start < end; start++) {
|
||||
if (test_bit(start, job->done_bitmap)) {
|
||||
for (; start < end; start += job->cluster_size) {
|
||||
if (test_bit(start / job->cluster_size, job->done_bitmap)) {
|
||||
trace_backup_do_cow_skip(job, start);
|
||||
continue; /* already copied */
|
||||
}
|
||||
|
||||
trace_backup_do_cow_process(job, start);
|
||||
|
||||
n = MIN(sectors_per_cluster,
|
||||
job->common.len / BDRV_SECTOR_SIZE -
|
||||
start * sectors_per_cluster);
|
||||
n = MIN(job->cluster_size, job->common.len - start);
|
||||
|
||||
if (!bounce_buffer) {
|
||||
bounce_buffer = blk_blockalign(blk, job->cluster_size);
|
||||
}
|
||||
iov.iov_base = bounce_buffer;
|
||||
iov.iov_len = n * BDRV_SECTOR_SIZE;
|
||||
iov.iov_len = n;
|
||||
qemu_iovec_init_external(&bounce_qiov, &iov, 1);
|
||||
|
||||
ret = blk_co_preadv(blk, start * job->cluster_size,
|
||||
bounce_qiov.size, &bounce_qiov,
|
||||
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);
|
||||
|
@ -146,10 +136,10 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
|||
}
|
||||
|
||||
if (buffer_is_zero(iov.iov_base, iov.iov_len)) {
|
||||
ret = blk_co_pwrite_zeroes(job->target, start * job->cluster_size,
|
||||
ret = blk_co_pwrite_zeroes(job->target, start,
|
||||
bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
|
||||
} else {
|
||||
ret = blk_co_pwritev(job->target, start * job->cluster_size,
|
||||
ret = blk_co_pwritev(job->target, start,
|
||||
bounce_qiov.size, &bounce_qiov,
|
||||
job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
|
||||
}
|
||||
|
@ -161,13 +151,13 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
|
|||
goto out;
|
||||
}
|
||||
|
||||
set_bit(start, job->done_bitmap);
|
||||
set_bit(start / job->cluster_size, job->done_bitmap);
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
job->sectors_read += n;
|
||||
job->common.offset += n * BDRV_SECTOR_SIZE;
|
||||
job->bytes_read += n;
|
||||
job->common.offset += n;
|
||||
}
|
||||
|
||||
out:
|
||||
|
@ -177,7 +167,7 @@ out:
|
|||
|
||||
cow_request_end(&cow_request);
|
||||
|
||||
trace_backup_do_cow_return(job, sector_num, nb_sectors, ret);
|
||||
trace_backup_do_cow_return(job, offset, bytes, ret);
|
||||
|
||||
qemu_co_rwlock_unlock(&job->flush_rwlock);
|
||||
|
||||
|
@ -190,14 +180,12 @@ static int coroutine_fn backup_before_write_notify(
|
|||
{
|
||||
BackupBlockJob *job = container_of(notifier, BackupBlockJob, before_write);
|
||||
BdrvTrackedRequest *req = opaque;
|
||||
int64_t sector_num = req->offset >> BDRV_SECTOR_BITS;
|
||||
int nb_sectors = req->bytes >> BDRV_SECTOR_BITS;
|
||||
|
||||
assert(req->bs == blk_bs(job->common.blk));
|
||||
assert((req->offset & (BDRV_SECTOR_SIZE - 1)) == 0);
|
||||
assert((req->bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
|
||||
assert(QEMU_IS_ALIGNED(req->offset, BDRV_SECTOR_SIZE));
|
||||
assert(QEMU_IS_ALIGNED(req->bytes, BDRV_SECTOR_SIZE));
|
||||
|
||||
return backup_do_cow(job, sector_num, nb_sectors, NULL, true);
|
||||
return backup_do_cow(job, req->offset, req->bytes, NULL, true);
|
||||
}
|
||||
|
||||
static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
||||
|
@ -208,7 +196,7 @@ static void backup_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|||
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
|
||||
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
|
||||
}
|
||||
|
||||
static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
|
||||
|
@ -275,32 +263,29 @@ void backup_do_checkpoint(BlockJob *job, Error **errp)
|
|||
bitmap_zero(backup_job->done_bitmap, len);
|
||||
}
|
||||
|
||||
void backup_wait_for_overlapping_requests(BlockJob *job, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
|
||||
uint64_t bytes)
|
||||
{
|
||||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||
int64_t sectors_per_cluster = cluster_size_sectors(backup_job);
|
||||
int64_t start, end;
|
||||
|
||||
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
|
||||
|
||||
start = sector_num / sectors_per_cluster;
|
||||
end = DIV_ROUND_UP(sector_num + nb_sectors, sectors_per_cluster);
|
||||
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
|
||||
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
|
||||
wait_for_overlapping_requests(backup_job, start, end);
|
||||
}
|
||||
|
||||
void backup_cow_request_begin(CowRequest *req, BlockJob *job,
|
||||
int64_t sector_num,
|
||||
int nb_sectors)
|
||||
int64_t offset, uint64_t bytes)
|
||||
{
|
||||
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
|
||||
int64_t sectors_per_cluster = cluster_size_sectors(backup_job);
|
||||
int64_t start, end;
|
||||
|
||||
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
|
||||
|
||||
start = sector_num / sectors_per_cluster;
|
||||
end = DIV_ROUND_UP(sector_num + nb_sectors, sectors_per_cluster);
|
||||
start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
|
||||
end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
|
||||
cow_request_begin(req, backup_job, start, end);
|
||||
}
|
||||
|
||||
|
@ -359,8 +344,8 @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
|
|||
*/
|
||||
if (job->common.speed) {
|
||||
uint64_t delay_ns = ratelimit_calculate_delay(&job->limit,
|
||||
job->sectors_read);
|
||||
job->sectors_read = 0;
|
||||
job->bytes_read);
|
||||
job->bytes_read = 0;
|
||||
block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, delay_ns);
|
||||
} else {
|
||||
block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, 0);
|
||||
|
@ -379,11 +364,10 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||
int ret = 0;
|
||||
int clusters_per_iter;
|
||||
uint32_t granularity;
|
||||
int64_t sector;
|
||||
int64_t offset;
|
||||
int64_t cluster;
|
||||
int64_t end;
|
||||
int64_t last_cluster = -1;
|
||||
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||
BdrvDirtyBitmapIter *dbi;
|
||||
|
||||
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap);
|
||||
|
@ -391,8 +375,8 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||
dbi = bdrv_dirty_iter_new(job->sync_bitmap, 0);
|
||||
|
||||
/* Find the next dirty sector(s) */
|
||||
while ((sector = bdrv_dirty_iter_next(dbi)) != -1) {
|
||||
cluster = sector / sectors_per_cluster;
|
||||
while ((offset = bdrv_dirty_iter_next(dbi) * BDRV_SECTOR_SIZE) >= 0) {
|
||||
cluster = offset / job->cluster_size;
|
||||
|
||||
/* Fake progress updates for any clusters we skipped */
|
||||
if (cluster != last_cluster + 1) {
|
||||
|
@ -405,8 +389,8 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||
if (yield_and_check(job)) {
|
||||
goto out;
|
||||
}
|
||||
ret = backup_do_cow(job, cluster * sectors_per_cluster,
|
||||
sectors_per_cluster, &error_is_read,
|
||||
ret = backup_do_cow(job, cluster * job->cluster_size,
|
||||
job->cluster_size, &error_is_read,
|
||||
false);
|
||||
if ((ret < 0) &&
|
||||
backup_error_action(job, error_is_read, -ret) ==
|
||||
|
@ -419,7 +403,8 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
|
|||
/* If the bitmap granularity is smaller than the backup granularity,
|
||||
* we need to advance the iterator pointer to the next cluster. */
|
||||
if (granularity < job->cluster_size) {
|
||||
bdrv_set_dirty_iter(dbi, cluster * sectors_per_cluster);
|
||||
bdrv_set_dirty_iter(dbi,
|
||||
cluster * job->cluster_size / BDRV_SECTOR_SIZE);
|
||||
}
|
||||
|
||||
last_cluster = cluster - 1;
|
||||
|
@ -441,17 +426,14 @@ static void coroutine_fn backup_run(void *opaque)
|
|||
BackupBlockJob *job = opaque;
|
||||
BackupCompleteData *data;
|
||||
BlockDriverState *bs = blk_bs(job->common.blk);
|
||||
int64_t start, end;
|
||||
int64_t sectors_per_cluster = cluster_size_sectors(job);
|
||||
int64_t offset;
|
||||
int ret = 0;
|
||||
|
||||
QLIST_INIT(&job->inflight_reqs);
|
||||
qemu_co_rwlock_init(&job->flush_rwlock);
|
||||
|
||||
start = 0;
|
||||
end = DIV_ROUND_UP(job->common.len, job->cluster_size);
|
||||
|
||||
job->done_bitmap = bitmap_new(end);
|
||||
job->done_bitmap = bitmap_new(DIV_ROUND_UP(job->common.len,
|
||||
job->cluster_size));
|
||||
|
||||
job->before_write.notify = backup_before_write_notify;
|
||||
bdrv_add_before_write_notifier(bs, &job->before_write);
|
||||
|
@ -466,7 +448,8 @@ static void coroutine_fn backup_run(void *opaque)
|
|||
ret = backup_run_incremental(job);
|
||||
} else {
|
||||
/* Both FULL and TOP SYNC_MODE's require copying.. */
|
||||
for (; start < end; start++) {
|
||||
for (offset = 0; offset < job->common.len;
|
||||
offset += job->cluster_size) {
|
||||
bool error_is_read;
|
||||
int alloced = 0;
|
||||
|
||||
|
@ -475,12 +458,13 @@ static void coroutine_fn backup_run(void *opaque)
|
|||
}
|
||||
|
||||
if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
|
||||
int i, n;
|
||||
int i;
|
||||
int64_t n;
|
||||
|
||||
/* Check to see if these blocks are already in the
|
||||
* backing file. */
|
||||
|
||||
for (i = 0; i < sectors_per_cluster;) {
|
||||
for (i = 0; i < job->cluster_size;) {
|
||||
/* bdrv_is_allocated() only returns true/false based
|
||||
* on the first set of sectors it comes across that
|
||||
* are are all in the same state.
|
||||
|
@ -488,9 +472,8 @@ static void coroutine_fn backup_run(void *opaque)
|
|||
* backup cluster length. We end up copying more than
|
||||
* needed but at some point that is always the case. */
|
||||
alloced =
|
||||
bdrv_is_allocated(bs,
|
||||
start * sectors_per_cluster + i,
|
||||
sectors_per_cluster - i, &n);
|
||||
bdrv_is_allocated(bs, offset + i,
|
||||
job->cluster_size - i, &n);
|
||||
i += n;
|
||||
|
||||
if (alloced || n == 0) {
|
||||
|
@ -508,9 +491,8 @@ static void coroutine_fn backup_run(void *opaque)
|
|||
if (alloced < 0) {
|
||||
ret = alloced;
|
||||
} else {
|
||||
ret = backup_do_cow(job, start * sectors_per_cluster,
|
||||
sectors_per_cluster, &error_is_read,
|
||||
false);
|
||||
ret = backup_do_cow(job, offset, job->cluster_size,
|
||||
&error_is_read, false);
|
||||
}
|
||||
if (ret < 0) {
|
||||
/* Depending on error action, fail now or retry cluster */
|
||||
|
@ -519,7 +501,7 @@ static void coroutine_fn backup_run(void *opaque)
|
|||
if (action == BLOCK_ERROR_ACTION_REPORT) {
|
||||
break;
|
||||
} else {
|
||||
start--;
|
||||
offset -= job->cluster_size;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -641,6 +641,16 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
|
|||
return bdrv_co_pdiscard(bs->file->bs, offset, bytes);
|
||||
}
|
||||
|
||||
static int64_t coroutine_fn blkdebug_co_get_block_status(
|
||||
BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
|
||||
BlockDriverState **file)
|
||||
{
|
||||
*pnum = nb_sectors;
|
||||
*file = bs->file->bs;
|
||||
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
|
||||
(sector_num << BDRV_SECTOR_BITS);
|
||||
}
|
||||
|
||||
static void blkdebug_close(BlockDriverState *bs)
|
||||
{
|
||||
BDRVBlkdebugState *s = bs->opaque;
|
||||
|
@ -915,6 +925,7 @@ static BlockDriver bdrv_blkdebug = {
|
|||
.bdrv_co_flush_to_disk = blkdebug_co_flush,
|
||||
.bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes,
|
||||
.bdrv_co_pdiscard = blkdebug_co_pdiscard,
|
||||
.bdrv_co_get_block_status = blkdebug_co_get_block_status,
|
||||
|
||||
.bdrv_debug_event = blkdebug_debug_event,
|
||||
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
|
||||
|
|
|
@ -47,26 +47,25 @@ typedef struct CommitBlockJob {
|
|||
} CommitBlockJob;
|
||||
|
||||
static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
int64_t offset, uint64_t bytes,
|
||||
void *buf)
|
||||
{
|
||||
int ret = 0;
|
||||
QEMUIOVector qiov;
|
||||
struct iovec iov = {
|
||||
.iov_base = buf,
|
||||
.iov_len = nb_sectors * BDRV_SECTOR_SIZE,
|
||||
.iov_len = bytes,
|
||||
};
|
||||
|
||||
assert(bytes < SIZE_MAX);
|
||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||
|
||||
ret = blk_co_preadv(bs, sector_num * BDRV_SECTOR_SIZE,
|
||||
qiov.size, &qiov, 0);
|
||||
ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = blk_co_pwritev(base, sector_num * BDRV_SECTOR_SIZE,
|
||||
qiov.size, &qiov, 0);
|
||||
ret = blk_co_pwritev(base, offset, qiov.size, &qiov, 0);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -144,17 +143,16 @@ static void coroutine_fn commit_run(void *opaque)
|
|||
{
|
||||
CommitBlockJob *s = opaque;
|
||||
CommitCompleteData *data;
|
||||
int64_t sector_num, end;
|
||||
int64_t offset;
|
||||
uint64_t delay_ns = 0;
|
||||
int ret = 0;
|
||||
int n = 0;
|
||||
int64_t n = 0; /* bytes */
|
||||
void *buf = NULL;
|
||||
int bytes_written = 0;
|
||||
int64_t base_len;
|
||||
|
||||
ret = s->common.len = blk_getlength(s->top);
|
||||
|
||||
|
||||
if (s->common.len < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
@ -171,10 +169,9 @@ static void coroutine_fn commit_run(void *opaque)
|
|||
}
|
||||
}
|
||||
|
||||
end = s->common.len >> BDRV_SECTOR_BITS;
|
||||
buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE);
|
||||
|
||||
for (sector_num = 0; sector_num < end; sector_num += n) {
|
||||
for (offset = 0; offset < s->common.len; offset += n) {
|
||||
bool copy;
|
||||
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
|
@ -186,14 +183,12 @@ static void coroutine_fn commit_run(void *opaque)
|
|||
}
|
||||
/* Copy if allocated above the base */
|
||||
ret = bdrv_is_allocated_above(blk_bs(s->top), blk_bs(s->base),
|
||||
sector_num,
|
||||
COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE,
|
||||
&n);
|
||||
offset, COMMIT_BUFFER_SIZE, &n);
|
||||
copy = (ret == 1);
|
||||
trace_commit_one_iteration(s, sector_num, n, ret);
|
||||
trace_commit_one_iteration(s, offset, n, ret);
|
||||
if (copy) {
|
||||
ret = commit_populate(s->top, s->base, sector_num, n, buf);
|
||||
bytes_written += n * BDRV_SECTOR_SIZE;
|
||||
ret = commit_populate(s->top, s->base, offset, n, buf);
|
||||
bytes_written += n;
|
||||
}
|
||||
if (ret < 0) {
|
||||
BlockErrorAction action =
|
||||
|
@ -206,7 +201,7 @@ static void coroutine_fn commit_run(void *opaque)
|
|||
}
|
||||
}
|
||||
/* Publish progress */
|
||||
s->common.offset += n * BDRV_SECTOR_SIZE;
|
||||
s->common.offset += n;
|
||||
|
||||
if (copy && s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
|
@ -231,7 +226,7 @@ static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|||
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
|
||||
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
|
||||
}
|
||||
|
||||
static const BlockJobDriver commit_job_driver = {
|
||||
|
@ -253,7 +248,7 @@ static int64_t coroutine_fn bdrv_commit_top_get_block_status(
|
|||
{
|
||||
*pnum = nb_sectors;
|
||||
*file = bs->backing->bs;
|
||||
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA |
|
||||
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
|
||||
(sector_num << BDRV_SECTOR_BITS);
|
||||
}
|
||||
|
||||
|
@ -444,7 +439,7 @@ fail:
|
|||
}
|
||||
|
||||
|
||||
#define COMMIT_BUF_SECTORS 2048
|
||||
#define COMMIT_BUF_SIZE (2048 * BDRV_SECTOR_SIZE)
|
||||
|
||||
/* commit COW file into the raw image */
|
||||
int bdrv_commit(BlockDriverState *bs)
|
||||
|
@ -453,8 +448,9 @@ int bdrv_commit(BlockDriverState *bs)
|
|||
BlockDriverState *backing_file_bs = NULL;
|
||||
BlockDriverState *commit_top_bs = NULL;
|
||||
BlockDriver *drv = bs->drv;
|
||||
int64_t sector, total_sectors, length, backing_length;
|
||||
int n, ro, open_flags;
|
||||
int64_t offset, length, backing_length;
|
||||
int ro, open_flags;
|
||||
int64_t n;
|
||||
int ret = 0;
|
||||
uint8_t *buf = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
@ -532,30 +528,26 @@ int bdrv_commit(BlockDriverState *bs)
|
|||
}
|
||||
}
|
||||
|
||||
total_sectors = length >> BDRV_SECTOR_BITS;
|
||||
|
||||
/* blk_try_blockalign() for src will choose an alignment that works for
|
||||
* backing as well, so no need to compare the alignment manually. */
|
||||
buf = blk_try_blockalign(src, COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE);
|
||||
buf = blk_try_blockalign(src, COMMIT_BUF_SIZE);
|
||||
if (buf == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto ro_cleanup;
|
||||
}
|
||||
|
||||
for (sector = 0; sector < total_sectors; sector += n) {
|
||||
ret = bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n);
|
||||
for (offset = 0; offset < length; offset += n) {
|
||||
ret = bdrv_is_allocated(bs, offset, COMMIT_BUF_SIZE, &n);
|
||||
if (ret < 0) {
|
||||
goto ro_cleanup;
|
||||
}
|
||||
if (ret) {
|
||||
ret = blk_pread(src, sector * BDRV_SECTOR_SIZE, buf,
|
||||
n * BDRV_SECTOR_SIZE);
|
||||
ret = blk_pread(src, offset, buf, n);
|
||||
if (ret < 0) {
|
||||
goto ro_cleanup;
|
||||
}
|
||||
|
||||
ret = blk_pwrite(backing, sector * BDRV_SECTOR_SIZE, buf,
|
||||
n * BDRV_SECTOR_SIZE, 0);
|
||||
ret = blk_pwrite(backing, offset, buf, n, 0);
|
||||
if (ret < 0) {
|
||||
goto ro_cleanup;
|
||||
}
|
||||
|
|
102
block/io.c
102
block/io.c
|
@ -418,27 +418,6 @@ static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
|
|||
req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Round a region to cluster boundaries (sector-based)
|
||||
*/
|
||||
void bdrv_round_sectors_to_clusters(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
int64_t *cluster_sector_num,
|
||||
int *cluster_nb_sectors)
|
||||
{
|
||||
BlockDriverInfo bdi;
|
||||
|
||||
if (bdrv_get_info(bs, &bdi) < 0 || bdi.cluster_size == 0) {
|
||||
*cluster_sector_num = sector_num;
|
||||
*cluster_nb_sectors = nb_sectors;
|
||||
} else {
|
||||
int64_t c = bdi.cluster_size / BDRV_SECTOR_SIZE;
|
||||
*cluster_sector_num = QEMU_ALIGN_DOWN(sector_num, c);
|
||||
*cluster_nb_sectors = QEMU_ALIGN_UP(sector_num - *cluster_sector_num +
|
||||
nb_sectors, c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Round a region to cluster boundaries
|
||||
*/
|
||||
|
@ -1054,17 +1033,18 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
|
|||
}
|
||||
|
||||
if (flags & BDRV_REQ_COPY_ON_READ) {
|
||||
int64_t start_sector = offset >> BDRV_SECTOR_BITS;
|
||||
int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE);
|
||||
unsigned int nb_sectors = end_sector - start_sector;
|
||||
int pnum;
|
||||
/* TODO: Simplify further once bdrv_is_allocated no longer
|
||||
* requires sector alignment */
|
||||
int64_t start = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE);
|
||||
int64_t end = QEMU_ALIGN_UP(offset + bytes, BDRV_SECTOR_SIZE);
|
||||
int64_t pnum;
|
||||
|
||||
ret = bdrv_is_allocated(bs, start_sector, nb_sectors, &pnum);
|
||||
ret = bdrv_is_allocated(bs, start, end - start, &pnum);
|
||||
if (ret < 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ret || pnum != nb_sectors) {
|
||||
if (!ret || pnum != end - start) {
|
||||
ret = bdrv_co_do_copy_on_readv(child, offset, bytes, qiov);
|
||||
goto out;
|
||||
}
|
||||
|
@ -1734,6 +1714,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
|||
int64_t n;
|
||||
int64_t ret, ret2;
|
||||
|
||||
*file = NULL;
|
||||
total_sectors = bdrv_nb_sectors(bs);
|
||||
if (total_sectors < 0) {
|
||||
return total_sectors;
|
||||
|
@ -1757,11 +1738,11 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
|||
}
|
||||
if (bs->drv->protocol_name) {
|
||||
ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE);
|
||||
*file = bs;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
*file = NULL;
|
||||
bdrv_inc_in_flight(bs);
|
||||
ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum,
|
||||
file);
|
||||
|
@ -1771,7 +1752,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
|||
}
|
||||
|
||||
if (ret & BDRV_BLOCK_RAW) {
|
||||
assert(ret & BDRV_BLOCK_OFFSET_VALID);
|
||||
assert(ret & BDRV_BLOCK_OFFSET_VALID && *file);
|
||||
ret = bdrv_co_get_block_status(*file, ret >> BDRV_SECTOR_BITS,
|
||||
*pnum, pnum, file);
|
||||
goto out;
|
||||
|
@ -1920,59 +1901,72 @@ int64_t bdrv_get_block_status(BlockDriverState *bs,
|
|||
sector_num, nb_sectors, pnum, file);
|
||||
}
|
||||
|
||||
int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset,
|
||||
int64_t bytes, int64_t *pnum)
|
||||
{
|
||||
BlockDriverState *file;
|
||||
int64_t ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum,
|
||||
&file);
|
||||
int64_t sector_num = offset >> BDRV_SECTOR_BITS;
|
||||
int nb_sectors = bytes >> BDRV_SECTOR_BITS;
|
||||
int64_t ret;
|
||||
int psectors;
|
||||
|
||||
assert(QEMU_IS_ALIGNED(offset, BDRV_SECTOR_SIZE));
|
||||
assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE) && bytes < INT_MAX);
|
||||
ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &psectors,
|
||||
&file);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
if (pnum) {
|
||||
*pnum = psectors * BDRV_SECTOR_SIZE;
|
||||
}
|
||||
return !!(ret & BDRV_BLOCK_ALLOCATED);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP]
|
||||
*
|
||||
* Return true if the given sector is allocated in any image between
|
||||
* BASE and TOP (inclusive). BASE can be NULL to check if the given
|
||||
* sector is allocated in any image of the chain. Return false otherwise.
|
||||
* Return true if (a prefix of) the given range is allocated in any image
|
||||
* between BASE and TOP (inclusive). BASE can be NULL to check if the given
|
||||
* offset is allocated in any image of the chain. Return false otherwise,
|
||||
* or negative errno on failure.
|
||||
*
|
||||
* 'pnum' is set to the number of sectors (including and immediately following
|
||||
* the specified sector) that are known to be in the same
|
||||
* allocated/unallocated state.
|
||||
* 'pnum' is set to the number of bytes (including and immediately
|
||||
* following the specified offset) that are known to be in the same
|
||||
* allocated/unallocated state. Note that a subsequent call starting
|
||||
* at 'offset + *pnum' may return the same allocation status (in other
|
||||
* words, the result is not necessarily the maximum possible range);
|
||||
* but 'pnum' will only be 0 when end of file is reached.
|
||||
*
|
||||
*/
|
||||
int bdrv_is_allocated_above(BlockDriverState *top,
|
||||
BlockDriverState *base,
|
||||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum)
|
||||
int64_t offset, int64_t bytes, int64_t *pnum)
|
||||
{
|
||||
BlockDriverState *intermediate;
|
||||
int ret, n = nb_sectors;
|
||||
int ret;
|
||||
int64_t n = bytes;
|
||||
|
||||
intermediate = top;
|
||||
while (intermediate && intermediate != base) {
|
||||
int pnum_inter;
|
||||
ret = bdrv_is_allocated(intermediate, sector_num, nb_sectors,
|
||||
&pnum_inter);
|
||||
int64_t pnum_inter;
|
||||
int64_t size_inter;
|
||||
|
||||
ret = bdrv_is_allocated(intermediate, offset, bytes, &pnum_inter);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
} else if (ret) {
|
||||
}
|
||||
if (ret) {
|
||||
*pnum = pnum_inter;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* [sector_num, nb_sectors] is unallocated on top but intermediate
|
||||
* might have
|
||||
*
|
||||
* [sector_num+x, nr_sectors] allocated.
|
||||
*/
|
||||
size_inter = bdrv_getlength(intermediate);
|
||||
if (size_inter < 0) {
|
||||
return size_inter;
|
||||
}
|
||||
if (n > pnum_inter &&
|
||||
(intermediate == top ||
|
||||
sector_num + pnum_inter < intermediate->total_sectors)) {
|
||||
(intermediate == top || offset + pnum_inter < size_inter)) {
|
||||
n = pnum_inter;
|
||||
}
|
||||
|
||||
|
|
310
block/mirror.c
310
block/mirror.c
|
@ -24,9 +24,8 @@
|
|||
|
||||
#define SLICE_TIME 100000000ULL /* ns */
|
||||
#define MAX_IN_FLIGHT 16
|
||||
#define MAX_IO_SECTORS ((1 << 20) >> BDRV_SECTOR_BITS) /* 1 Mb */
|
||||
#define DEFAULT_MIRROR_BUF_SIZE \
|
||||
(MAX_IN_FLIGHT * MAX_IO_SECTORS * BDRV_SECTOR_SIZE)
|
||||
#define MAX_IO_BYTES (1 << 20) /* 1 Mb */
|
||||
#define DEFAULT_MIRROR_BUF_SIZE (MAX_IN_FLIGHT * MAX_IO_BYTES)
|
||||
|
||||
/* The mirroring buffer is a list of granularity-sized chunks.
|
||||
* Free chunks are organized in a list.
|
||||
|
@ -67,11 +66,11 @@ typedef struct MirrorBlockJob {
|
|||
uint64_t last_pause_ns;
|
||||
unsigned long *in_flight_bitmap;
|
||||
int in_flight;
|
||||
int64_t sectors_in_flight;
|
||||
int64_t bytes_in_flight;
|
||||
int ret;
|
||||
bool unmap;
|
||||
bool waiting_for_io;
|
||||
int target_cluster_sectors;
|
||||
int target_cluster_size;
|
||||
int max_iov;
|
||||
bool initial_zeroing_ongoing;
|
||||
} MirrorBlockJob;
|
||||
|
@ -79,8 +78,8 @@ typedef struct MirrorBlockJob {
|
|||
typedef struct MirrorOp {
|
||||
MirrorBlockJob *s;
|
||||
QEMUIOVector qiov;
|
||||
int64_t sector_num;
|
||||
int nb_sectors;
|
||||
int64_t offset;
|
||||
uint64_t bytes;
|
||||
} MirrorOp;
|
||||
|
||||
static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read,
|
||||
|
@ -101,12 +100,12 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
|
|||
MirrorBlockJob *s = op->s;
|
||||
struct iovec *iov;
|
||||
int64_t chunk_num;
|
||||
int i, nb_chunks, sectors_per_chunk;
|
||||
int i, nb_chunks;
|
||||
|
||||
trace_mirror_iteration_done(s, op->sector_num, op->nb_sectors, ret);
|
||||
trace_mirror_iteration_done(s, op->offset, op->bytes, ret);
|
||||
|
||||
s->in_flight--;
|
||||
s->sectors_in_flight -= op->nb_sectors;
|
||||
s->bytes_in_flight -= op->bytes;
|
||||
iov = op->qiov.iov;
|
||||
for (i = 0; i < op->qiov.niov; i++) {
|
||||
MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base;
|
||||
|
@ -114,16 +113,15 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
|
|||
s->buf_free_count++;
|
||||
}
|
||||
|
||||
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||
chunk_num = op->sector_num / sectors_per_chunk;
|
||||
nb_chunks = DIV_ROUND_UP(op->nb_sectors, sectors_per_chunk);
|
||||
chunk_num = op->offset / s->granularity;
|
||||
nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity);
|
||||
bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks);
|
||||
if (ret >= 0) {
|
||||
if (s->cow_bitmap) {
|
||||
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
|
||||
}
|
||||
if (!s->initial_zeroing_ongoing) {
|
||||
s->common.offset += (uint64_t)op->nb_sectors * BDRV_SECTOR_SIZE;
|
||||
s->common.offset += op->bytes;
|
||||
}
|
||||
}
|
||||
qemu_iovec_destroy(&op->qiov);
|
||||
|
@ -143,7 +141,8 @@ static void mirror_write_complete(void *opaque, int ret)
|
|||
if (ret < 0) {
|
||||
BlockErrorAction action;
|
||||
|
||||
bdrv_set_dirty_bitmap(s->dirty_bitmap, op->sector_num, op->nb_sectors);
|
||||
bdrv_set_dirty_bitmap(s->dirty_bitmap, op->offset >> BDRV_SECTOR_BITS,
|
||||
op->bytes >> BDRV_SECTOR_BITS);
|
||||
action = mirror_error_action(s, false, -ret);
|
||||
if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) {
|
||||
s->ret = ret;
|
||||
|
@ -162,7 +161,8 @@ static void mirror_read_complete(void *opaque, int ret)
|
|||
if (ret < 0) {
|
||||
BlockErrorAction action;
|
||||
|
||||
bdrv_set_dirty_bitmap(s->dirty_bitmap, op->sector_num, op->nb_sectors);
|
||||
bdrv_set_dirty_bitmap(s->dirty_bitmap, op->offset >> BDRV_SECTOR_BITS,
|
||||
op->bytes >> BDRV_SECTOR_BITS);
|
||||
action = mirror_error_action(s, true, -ret);
|
||||
if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) {
|
||||
s->ret = ret;
|
||||
|
@ -170,56 +170,53 @@ static void mirror_read_complete(void *opaque, int ret)
|
|||
|
||||
mirror_iteration_done(op, ret);
|
||||
} else {
|
||||
blk_aio_pwritev(s->target, op->sector_num * BDRV_SECTOR_SIZE, &op->qiov,
|
||||
blk_aio_pwritev(s->target, op->offset, &op->qiov,
|
||||
0, mirror_write_complete, op);
|
||||
}
|
||||
aio_context_release(blk_get_aio_context(s->common.blk));
|
||||
}
|
||||
|
||||
static inline void mirror_clip_sectors(MirrorBlockJob *s,
|
||||
int64_t sector_num,
|
||||
int *nb_sectors)
|
||||
/* Clip bytes relative to offset to not exceed end-of-file */
|
||||
static inline int64_t mirror_clip_bytes(MirrorBlockJob *s,
|
||||
int64_t offset,
|
||||
int64_t bytes)
|
||||
{
|
||||
*nb_sectors = MIN(*nb_sectors,
|
||||
s->bdev_length / BDRV_SECTOR_SIZE - sector_num);
|
||||
return MIN(bytes, s->bdev_length - offset);
|
||||
}
|
||||
|
||||
/* Round sector_num and/or nb_sectors to target cluster if COW is needed, and
|
||||
* return the offset of the adjusted tail sector against original. */
|
||||
static int mirror_cow_align(MirrorBlockJob *s,
|
||||
int64_t *sector_num,
|
||||
int *nb_sectors)
|
||||
/* Round offset and/or bytes to target cluster if COW is needed, and
|
||||
* return the offset of the adjusted tail against original. */
|
||||
static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset,
|
||||
uint64_t *bytes)
|
||||
{
|
||||
bool need_cow;
|
||||
int ret = 0;
|
||||
int chunk_sectors = s->granularity >> BDRV_SECTOR_BITS;
|
||||
int64_t align_sector_num = *sector_num;
|
||||
int align_nb_sectors = *nb_sectors;
|
||||
int max_sectors = chunk_sectors * s->max_iov;
|
||||
int64_t align_offset = *offset;
|
||||
unsigned int align_bytes = *bytes;
|
||||
int max_bytes = s->granularity * s->max_iov;
|
||||
|
||||
need_cow = !test_bit(*sector_num / chunk_sectors, s->cow_bitmap);
|
||||
need_cow |= !test_bit((*sector_num + *nb_sectors - 1) / chunk_sectors,
|
||||
assert(*bytes < INT_MAX);
|
||||
need_cow = !test_bit(*offset / s->granularity, s->cow_bitmap);
|
||||
need_cow |= !test_bit((*offset + *bytes - 1) / s->granularity,
|
||||
s->cow_bitmap);
|
||||
if (need_cow) {
|
||||
bdrv_round_sectors_to_clusters(blk_bs(s->target), *sector_num,
|
||||
*nb_sectors, &align_sector_num,
|
||||
&align_nb_sectors);
|
||||
bdrv_round_to_clusters(blk_bs(s->target), *offset, *bytes,
|
||||
&align_offset, &align_bytes);
|
||||
}
|
||||
|
||||
if (align_nb_sectors > max_sectors) {
|
||||
align_nb_sectors = max_sectors;
|
||||
if (align_bytes > max_bytes) {
|
||||
align_bytes = max_bytes;
|
||||
if (need_cow) {
|
||||
align_nb_sectors = QEMU_ALIGN_DOWN(align_nb_sectors,
|
||||
s->target_cluster_sectors);
|
||||
align_bytes = QEMU_ALIGN_DOWN(align_bytes, s->target_cluster_size);
|
||||
}
|
||||
}
|
||||
/* Clipping may result in align_nb_sectors unaligned to chunk boundary, but
|
||||
/* Clipping may result in align_bytes unaligned to chunk boundary, but
|
||||
* that doesn't matter because it's already the end of source image. */
|
||||
mirror_clip_sectors(s, align_sector_num, &align_nb_sectors);
|
||||
align_bytes = mirror_clip_bytes(s, align_offset, align_bytes);
|
||||
|
||||
ret = align_sector_num + align_nb_sectors - (*sector_num + *nb_sectors);
|
||||
*sector_num = align_sector_num;
|
||||
*nb_sectors = align_nb_sectors;
|
||||
ret = align_offset + align_bytes - (*offset + *bytes);
|
||||
*offset = align_offset;
|
||||
*bytes = align_bytes;
|
||||
assert(ret >= 0);
|
||||
return ret;
|
||||
}
|
||||
|
@ -233,50 +230,51 @@ static inline void mirror_wait_for_io(MirrorBlockJob *s)
|
|||
}
|
||||
|
||||
/* Submit async read while handling COW.
|
||||
* Returns: The number of sectors copied after and including sector_num,
|
||||
* excluding any sectors copied prior to sector_num due to alignment.
|
||||
* This will be nb_sectors if no alignment is necessary, or
|
||||
* (new_end - sector_num) if tail is rounded up or down due to
|
||||
* Returns: The number of bytes copied after and including offset,
|
||||
* excluding any bytes copied prior to offset due to alignment.
|
||||
* This will be @bytes if no alignment is necessary, or
|
||||
* (new_end - offset) if tail is rounded up or down due to
|
||||
* alignment or buffer limit.
|
||||
*/
|
||||
static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num,
|
||||
int nb_sectors)
|
||||
static uint64_t mirror_do_read(MirrorBlockJob *s, int64_t offset,
|
||||
uint64_t bytes)
|
||||
{
|
||||
BlockBackend *source = s->common.blk;
|
||||
int sectors_per_chunk, nb_chunks;
|
||||
int ret;
|
||||
int nb_chunks;
|
||||
uint64_t ret;
|
||||
MirrorOp *op;
|
||||
int max_sectors;
|
||||
uint64_t max_bytes;
|
||||
|
||||
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||
max_sectors = sectors_per_chunk * s->max_iov;
|
||||
max_bytes = s->granularity * s->max_iov;
|
||||
|
||||
/* We can only handle as much as buf_size at a time. */
|
||||
nb_sectors = MIN(s->buf_size >> BDRV_SECTOR_BITS, nb_sectors);
|
||||
nb_sectors = MIN(max_sectors, nb_sectors);
|
||||
assert(nb_sectors);
|
||||
ret = nb_sectors;
|
||||
bytes = MIN(s->buf_size, MIN(max_bytes, bytes));
|
||||
assert(bytes);
|
||||
assert(bytes < BDRV_REQUEST_MAX_BYTES);
|
||||
ret = bytes;
|
||||
|
||||
if (s->cow_bitmap) {
|
||||
ret += mirror_cow_align(s, §or_num, &nb_sectors);
|
||||
ret += mirror_cow_align(s, &offset, &bytes);
|
||||
}
|
||||
assert(nb_sectors << BDRV_SECTOR_BITS <= s->buf_size);
|
||||
/* The sector range must meet granularity because:
|
||||
assert(bytes <= s->buf_size);
|
||||
/* The offset is granularity-aligned because:
|
||||
* 1) Caller passes in aligned values;
|
||||
* 2) mirror_cow_align is used only when target cluster is larger. */
|
||||
assert(!(sector_num % sectors_per_chunk));
|
||||
nb_chunks = DIV_ROUND_UP(nb_sectors, sectors_per_chunk);
|
||||
assert(QEMU_IS_ALIGNED(offset, s->granularity));
|
||||
/* The range is sector-aligned, since bdrv_getlength() rounds up. */
|
||||
assert(QEMU_IS_ALIGNED(bytes, BDRV_SECTOR_SIZE));
|
||||
nb_chunks = DIV_ROUND_UP(bytes, s->granularity);
|
||||
|
||||
while (s->buf_free_count < nb_chunks) {
|
||||
trace_mirror_yield_in_flight(s, sector_num, s->in_flight);
|
||||
trace_mirror_yield_in_flight(s, offset, s->in_flight);
|
||||
mirror_wait_for_io(s);
|
||||
}
|
||||
|
||||
/* Allocate a MirrorOp that is used as an AIO callback. */
|
||||
op = g_new(MirrorOp, 1);
|
||||
op->s = s;
|
||||
op->sector_num = sector_num;
|
||||
op->nb_sectors = nb_sectors;
|
||||
op->offset = offset;
|
||||
op->bytes = bytes;
|
||||
|
||||
/* Now make a QEMUIOVector taking enough granularity-sized chunks
|
||||
* from s->buf_free.
|
||||
|
@ -284,7 +282,7 @@ static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num,
|
|||
qemu_iovec_init(&op->qiov, nb_chunks);
|
||||
while (nb_chunks-- > 0) {
|
||||
MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free);
|
||||
size_t remaining = nb_sectors * BDRV_SECTOR_SIZE - op->qiov.size;
|
||||
size_t remaining = bytes - op->qiov.size;
|
||||
|
||||
QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next);
|
||||
s->buf_free_count--;
|
||||
|
@ -293,17 +291,16 @@ static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num,
|
|||
|
||||
/* Copy the dirty cluster. */
|
||||
s->in_flight++;
|
||||
s->sectors_in_flight += nb_sectors;
|
||||
trace_mirror_one_iteration(s, sector_num, nb_sectors);
|
||||
s->bytes_in_flight += bytes;
|
||||
trace_mirror_one_iteration(s, offset, bytes);
|
||||
|
||||
blk_aio_preadv(source, sector_num * BDRV_SECTOR_SIZE, &op->qiov, 0,
|
||||
mirror_read_complete, op);
|
||||
blk_aio_preadv(source, offset, &op->qiov, 0, mirror_read_complete, op);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mirror_do_zero_or_discard(MirrorBlockJob *s,
|
||||
int64_t sector_num,
|
||||
int nb_sectors,
|
||||
int64_t offset,
|
||||
uint64_t bytes,
|
||||
bool is_discard)
|
||||
{
|
||||
MirrorOp *op;
|
||||
|
@ -312,19 +309,17 @@ static void mirror_do_zero_or_discard(MirrorBlockJob *s,
|
|||
* so the freeing in mirror_iteration_done is nop. */
|
||||
op = g_new0(MirrorOp, 1);
|
||||
op->s = s;
|
||||
op->sector_num = sector_num;
|
||||
op->nb_sectors = nb_sectors;
|
||||
op->offset = offset;
|
||||
op->bytes = bytes;
|
||||
|
||||
s->in_flight++;
|
||||
s->sectors_in_flight += nb_sectors;
|
||||
s->bytes_in_flight += bytes;
|
||||
if (is_discard) {
|
||||
blk_aio_pdiscard(s->target, sector_num << BDRV_SECTOR_BITS,
|
||||
op->nb_sectors << BDRV_SECTOR_BITS,
|
||||
mirror_write_complete, op);
|
||||
blk_aio_pdiscard(s->target, offset,
|
||||
op->bytes, mirror_write_complete, op);
|
||||
} else {
|
||||
blk_aio_pwrite_zeroes(s->target, sector_num * BDRV_SECTOR_SIZE,
|
||||
op->nb_sectors * BDRV_SECTOR_SIZE,
|
||||
s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
|
||||
blk_aio_pwrite_zeroes(s->target, offset,
|
||||
op->bytes, s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
|
||||
mirror_write_complete, op);
|
||||
}
|
||||
}
|
||||
|
@ -332,29 +327,28 @@ static void mirror_do_zero_or_discard(MirrorBlockJob *s,
|
|||
static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||
{
|
||||
BlockDriverState *source = s->source;
|
||||
int64_t sector_num, first_chunk;
|
||||
int64_t offset, first_chunk;
|
||||
uint64_t delay_ns = 0;
|
||||
/* At least the first dirty chunk is mirrored in one iteration. */
|
||||
int nb_chunks = 1;
|
||||
int64_t end = s->bdev_length / BDRV_SECTOR_SIZE;
|
||||
int sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||
bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target));
|
||||
int max_io_sectors = MAX((s->buf_size >> BDRV_SECTOR_BITS) / MAX_IN_FLIGHT,
|
||||
MAX_IO_SECTORS);
|
||||
int max_io_bytes = MAX(s->buf_size / MAX_IN_FLIGHT, MAX_IO_BYTES);
|
||||
|
||||
bdrv_dirty_bitmap_lock(s->dirty_bitmap);
|
||||
sector_num = bdrv_dirty_iter_next(s->dbi);
|
||||
if (sector_num < 0) {
|
||||
offset = bdrv_dirty_iter_next(s->dbi) * BDRV_SECTOR_SIZE;
|
||||
if (offset < 0) {
|
||||
bdrv_set_dirty_iter(s->dbi, 0);
|
||||
sector_num = bdrv_dirty_iter_next(s->dbi);
|
||||
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap));
|
||||
assert(sector_num >= 0);
|
||||
offset = bdrv_dirty_iter_next(s->dbi) * BDRV_SECTOR_SIZE;
|
||||
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap) *
|
||||
BDRV_SECTOR_SIZE);
|
||||
assert(offset >= 0);
|
||||
}
|
||||
bdrv_dirty_bitmap_unlock(s->dirty_bitmap);
|
||||
|
||||
first_chunk = sector_num / sectors_per_chunk;
|
||||
first_chunk = offset / s->granularity;
|
||||
while (test_bit(first_chunk, s->in_flight_bitmap)) {
|
||||
trace_mirror_yield_in_flight(s, sector_num, s->in_flight);
|
||||
trace_mirror_yield_in_flight(s, offset, s->in_flight);
|
||||
mirror_wait_for_io(s);
|
||||
}
|
||||
|
||||
|
@ -363,25 +357,26 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||
/* Find the number of consective dirty chunks following the first dirty
|
||||
* one, and wait for in flight requests in them. */
|
||||
bdrv_dirty_bitmap_lock(s->dirty_bitmap);
|
||||
while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) {
|
||||
while (nb_chunks * s->granularity < s->buf_size) {
|
||||
int64_t next_dirty;
|
||||
int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk;
|
||||
int64_t next_chunk = next_sector / sectors_per_chunk;
|
||||
if (next_sector >= end ||
|
||||
!bdrv_get_dirty_locked(source, s->dirty_bitmap, next_sector)) {
|
||||
int64_t next_offset = offset + nb_chunks * s->granularity;
|
||||
int64_t next_chunk = next_offset / s->granularity;
|
||||
if (next_offset >= s->bdev_length ||
|
||||
!bdrv_get_dirty_locked(source, s->dirty_bitmap,
|
||||
next_offset >> BDRV_SECTOR_BITS)) {
|
||||
break;
|
||||
}
|
||||
if (test_bit(next_chunk, s->in_flight_bitmap)) {
|
||||
break;
|
||||
}
|
||||
|
||||
next_dirty = bdrv_dirty_iter_next(s->dbi);
|
||||
if (next_dirty > next_sector || next_dirty < 0) {
|
||||
next_dirty = bdrv_dirty_iter_next(s->dbi) * BDRV_SECTOR_SIZE;
|
||||
if (next_dirty > next_offset || next_dirty < 0) {
|
||||
/* The bitmap iterator's cache is stale, refresh it */
|
||||
bdrv_set_dirty_iter(s->dbi, next_sector);
|
||||
next_dirty = bdrv_dirty_iter_next(s->dbi);
|
||||
bdrv_set_dirty_iter(s->dbi, next_offset >> BDRV_SECTOR_BITS);
|
||||
next_dirty = bdrv_dirty_iter_next(s->dbi) * BDRV_SECTOR_SIZE;
|
||||
}
|
||||
assert(next_dirty == next_sector);
|
||||
assert(next_dirty == next_offset);
|
||||
nb_chunks++;
|
||||
}
|
||||
|
||||
|
@ -389,14 +384,16 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||
* calling bdrv_get_block_status_above could yield - if some blocks are
|
||||
* marked dirty in this window, we need to know.
|
||||
*/
|
||||
bdrv_reset_dirty_bitmap_locked(s->dirty_bitmap, sector_num,
|
||||
nb_chunks * sectors_per_chunk);
|
||||
bdrv_reset_dirty_bitmap_locked(s->dirty_bitmap, offset >> BDRV_SECTOR_BITS,
|
||||
nb_chunks * sectors_per_chunk);
|
||||
bdrv_dirty_bitmap_unlock(s->dirty_bitmap);
|
||||
|
||||
bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks);
|
||||
while (nb_chunks > 0 && sector_num < end) {
|
||||
bitmap_set(s->in_flight_bitmap, offset / s->granularity, nb_chunks);
|
||||
while (nb_chunks > 0 && offset < s->bdev_length) {
|
||||
int64_t ret;
|
||||
int io_sectors, io_sectors_acct;
|
||||
int io_sectors;
|
||||
unsigned int io_bytes;
|
||||
int64_t io_bytes_acct;
|
||||
BlockDriverState *file;
|
||||
enum MirrorMethod {
|
||||
MIRROR_METHOD_COPY,
|
||||
|
@ -404,27 +401,28 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||
MIRROR_METHOD_DISCARD
|
||||
} mirror_method = MIRROR_METHOD_COPY;
|
||||
|
||||
assert(!(sector_num % sectors_per_chunk));
|
||||
ret = bdrv_get_block_status_above(source, NULL, sector_num,
|
||||
assert(!(offset % s->granularity));
|
||||
ret = bdrv_get_block_status_above(source, NULL,
|
||||
offset >> BDRV_SECTOR_BITS,
|
||||
nb_chunks * sectors_per_chunk,
|
||||
&io_sectors, &file);
|
||||
io_bytes = io_sectors * BDRV_SECTOR_SIZE;
|
||||
if (ret < 0) {
|
||||
io_sectors = MIN(nb_chunks * sectors_per_chunk, max_io_sectors);
|
||||
io_bytes = MIN(nb_chunks * s->granularity, max_io_bytes);
|
||||
} else if (ret & BDRV_BLOCK_DATA) {
|
||||
io_sectors = MIN(io_sectors, max_io_sectors);
|
||||
io_bytes = MIN(io_bytes, max_io_bytes);
|
||||
}
|
||||
|
||||
io_sectors -= io_sectors % sectors_per_chunk;
|
||||
if (io_sectors < sectors_per_chunk) {
|
||||
io_sectors = sectors_per_chunk;
|
||||
io_bytes -= io_bytes % s->granularity;
|
||||
if (io_bytes < s->granularity) {
|
||||
io_bytes = s->granularity;
|
||||
} else if (ret >= 0 && !(ret & BDRV_BLOCK_DATA)) {
|
||||
int64_t target_sector_num;
|
||||
int target_nb_sectors;
|
||||
bdrv_round_sectors_to_clusters(blk_bs(s->target), sector_num,
|
||||
io_sectors, &target_sector_num,
|
||||
&target_nb_sectors);
|
||||
if (target_sector_num == sector_num &&
|
||||
target_nb_sectors == io_sectors) {
|
||||
int64_t target_offset;
|
||||
unsigned int target_bytes;
|
||||
bdrv_round_to_clusters(blk_bs(s->target), offset, io_bytes,
|
||||
&target_offset, &target_bytes);
|
||||
if (target_offset == offset &&
|
||||
target_bytes == io_bytes) {
|
||||
mirror_method = ret & BDRV_BLOCK_ZERO ?
|
||||
MIRROR_METHOD_ZERO :
|
||||
MIRROR_METHOD_DISCARD;
|
||||
|
@ -432,7 +430,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||
}
|
||||
|
||||
while (s->in_flight >= MAX_IN_FLIGHT) {
|
||||
trace_mirror_yield_in_flight(s, sector_num, s->in_flight);
|
||||
trace_mirror_yield_in_flight(s, offset, s->in_flight);
|
||||
mirror_wait_for_io(s);
|
||||
}
|
||||
|
||||
|
@ -440,30 +438,29 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
|||
return 0;
|
||||
}
|
||||
|
||||
mirror_clip_sectors(s, sector_num, &io_sectors);
|
||||
io_bytes = mirror_clip_bytes(s, offset, io_bytes);
|
||||
switch (mirror_method) {
|
||||
case MIRROR_METHOD_COPY:
|
||||
io_sectors = mirror_do_read(s, sector_num, io_sectors);
|
||||
io_sectors_acct = io_sectors;
|
||||
io_bytes = io_bytes_acct = mirror_do_read(s, offset, io_bytes);
|
||||
break;
|
||||
case MIRROR_METHOD_ZERO:
|
||||
case MIRROR_METHOD_DISCARD:
|
||||
mirror_do_zero_or_discard(s, sector_num, io_sectors,
|
||||
mirror_do_zero_or_discard(s, offset, io_bytes,
|
||||
mirror_method == MIRROR_METHOD_DISCARD);
|
||||
if (write_zeroes_ok) {
|
||||
io_sectors_acct = 0;
|
||||
io_bytes_acct = 0;
|
||||
} else {
|
||||
io_sectors_acct = io_sectors;
|
||||
io_bytes_acct = io_bytes;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
assert(io_sectors);
|
||||
sector_num += io_sectors;
|
||||
nb_chunks -= DIV_ROUND_UP(io_sectors, sectors_per_chunk);
|
||||
assert(io_bytes);
|
||||
offset += io_bytes;
|
||||
nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity);
|
||||
if (s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, io_sectors_acct);
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, io_bytes_acct);
|
||||
}
|
||||
}
|
||||
return delay_ns;
|
||||
|
@ -624,6 +621,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
|
|||
BlockDriverState *bs = s->source;
|
||||
BlockDriverState *target_bs = blk_bs(s->target);
|
||||
int ret, n;
|
||||
int64_t count;
|
||||
|
||||
end = s->bdev_length / BDRV_SECTOR_SIZE;
|
||||
|
||||
|
@ -652,7 +650,8 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
|
|||
continue;
|
||||
}
|
||||
|
||||
mirror_do_zero_or_discard(s, sector_num, nb_sectors, false);
|
||||
mirror_do_zero_or_discard(s, sector_num * BDRV_SECTOR_SIZE,
|
||||
nb_sectors * BDRV_SECTOR_SIZE, false);
|
||||
sector_num += nb_sectors;
|
||||
}
|
||||
|
||||
|
@ -672,11 +671,16 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
|
|||
return 0;
|
||||
}
|
||||
|
||||
ret = bdrv_is_allocated_above(bs, base, sector_num, nb_sectors, &n);
|
||||
ret = bdrv_is_allocated_above(bs, base, sector_num * BDRV_SECTOR_SIZE,
|
||||
nb_sectors * BDRV_SECTOR_SIZE, &count);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TODO: Relax this once bdrv_is_allocated_above and dirty
|
||||
* bitmaps no longer require sector alignment. */
|
||||
assert(QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE));
|
||||
n = count >> BDRV_SECTOR_BITS;
|
||||
assert(n > 0);
|
||||
if (ret == 1) {
|
||||
bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n);
|
||||
|
@ -712,7 +716,6 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||
char backing_filename[2]; /* we only need 2 characters because we are only
|
||||
checking for a NULL string */
|
||||
int ret = 0;
|
||||
int target_cluster_size = BDRV_SECTOR_SIZE;
|
||||
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
goto immediate_exit;
|
||||
|
@ -764,14 +767,15 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||
bdrv_get_backing_filename(target_bs, backing_filename,
|
||||
sizeof(backing_filename));
|
||||
if (!bdrv_get_info(target_bs, &bdi) && bdi.cluster_size) {
|
||||
target_cluster_size = bdi.cluster_size;
|
||||
s->target_cluster_size = bdi.cluster_size;
|
||||
} else {
|
||||
s->target_cluster_size = BDRV_SECTOR_SIZE;
|
||||
}
|
||||
if (backing_filename[0] && !target_bs->backing
|
||||
&& s->granularity < target_cluster_size) {
|
||||
s->buf_size = MAX(s->buf_size, target_cluster_size);
|
||||
if (backing_filename[0] && !target_bs->backing &&
|
||||
s->granularity < s->target_cluster_size) {
|
||||
s->buf_size = MAX(s->buf_size, s->target_cluster_size);
|
||||
s->cow_bitmap = bitmap_new(length);
|
||||
}
|
||||
s->target_cluster_sectors = target_cluster_size >> BDRV_SECTOR_BITS;
|
||||
s->max_iov = MIN(bs->bl.max_iov, target_bs->bl.max_iov);
|
||||
|
||||
s->buf = qemu_try_blockalign(bs, s->buf_size);
|
||||
|
@ -807,10 +811,10 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
|
||||
/* s->common.offset contains the number of bytes already processed so
|
||||
* far, cnt is the number of dirty sectors remaining and
|
||||
* s->sectors_in_flight is the number of sectors currently being
|
||||
* s->bytes_in_flight is the number of bytes currently being
|
||||
* processed; together those are the current total operation length */
|
||||
s->common.len = s->common.offset +
|
||||
(cnt + s->sectors_in_flight) * BDRV_SECTOR_SIZE;
|
||||
s->common.len = s->common.offset + s->bytes_in_flight +
|
||||
cnt * BDRV_SECTOR_SIZE;
|
||||
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
* periodically with no pending I/O so that bdrv_drain_all() returns.
|
||||
|
@ -822,7 +826,8 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||
s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
|
||||
if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 ||
|
||||
(cnt == 0 && s->in_flight > 0)) {
|
||||
trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight);
|
||||
trace_mirror_yield(s, cnt * BDRV_SECTOR_SIZE,
|
||||
s->buf_free_count, s->in_flight);
|
||||
mirror_wait_for_io(s);
|
||||
continue;
|
||||
} else if (cnt != 0) {
|
||||
|
@ -863,7 +868,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||
* whether to switch to target check one last time if I/O has
|
||||
* come in the meanwhile, and if not flush the data to disk.
|
||||
*/
|
||||
trace_mirror_before_drain(s, cnt);
|
||||
trace_mirror_before_drain(s, cnt * BDRV_SECTOR_SIZE);
|
||||
|
||||
bdrv_drained_begin(bs);
|
||||
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
|
||||
|
@ -882,7 +887,8 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||
}
|
||||
|
||||
ret = 0;
|
||||
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
|
||||
trace_mirror_before_sleep(s, cnt * BDRV_SECTOR_SIZE,
|
||||
s->synced, delay_ns);
|
||||
if (!s->synced) {
|
||||
block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns);
|
||||
if (block_job_is_cancelled(&s->common)) {
|
||||
|
@ -929,7 +935,7 @@ static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|||
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
|
||||
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
|
||||
}
|
||||
|
||||
static void mirror_complete(BlockJob *job, Error **errp)
|
||||
|
@ -1058,7 +1064,7 @@ static int64_t coroutine_fn bdrv_mirror_top_get_block_status(
|
|||
{
|
||||
*pnum = nb_sectors;
|
||||
*file = bs->backing->bs;
|
||||
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA |
|
||||
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
|
||||
(sector_num << BDRV_SECTOR_BITS);
|
||||
}
|
||||
|
||||
|
@ -1141,6 +1147,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
|||
}
|
||||
|
||||
assert ((granularity & (granularity - 1)) == 0);
|
||||
/* Granularity must be large enough for sector-based dirty bitmap */
|
||||
assert(granularity >= BDRV_SECTOR_SIZE);
|
||||
|
||||
if (buf_size < 0) {
|
||||
error_setg(errp, "Invalid parameter 'buf-size'");
|
||||
|
|
|
@ -259,7 +259,7 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
|
|||
*pnum = nb_sectors;
|
||||
*file = bs->file->bs;
|
||||
sector_num += s->offset / BDRV_SECTOR_SIZE;
|
||||
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA |
|
||||
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
|
||||
(sector_num << BDRV_SECTOR_BITS);
|
||||
}
|
||||
|
||||
|
|
|
@ -234,10 +234,14 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs,
|
|||
}
|
||||
|
||||
if (job) {
|
||||
backup_wait_for_overlapping_requests(child->bs->job, sector_num,
|
||||
remaining_sectors);
|
||||
backup_cow_request_begin(&req, child->bs->job, sector_num,
|
||||
remaining_sectors);
|
||||
uint64_t remaining_bytes = remaining_sectors * BDRV_SECTOR_SIZE;
|
||||
|
||||
backup_wait_for_overlapping_requests(child->bs->job,
|
||||
sector_num * BDRV_SECTOR_SIZE,
|
||||
remaining_bytes);
|
||||
backup_cow_request_begin(&req, child->bs->job,
|
||||
sector_num * BDRV_SECTOR_SIZE,
|
||||
remaining_bytes);
|
||||
ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors,
|
||||
qiov);
|
||||
backup_cow_request_end(&req);
|
||||
|
@ -260,7 +264,8 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
|
|||
BdrvChild *top = bs->file;
|
||||
BdrvChild *base = s->secondary_disk;
|
||||
BdrvChild *target;
|
||||
int ret, n;
|
||||
int ret;
|
||||
int64_t n;
|
||||
|
||||
ret = replication_get_io_status(s);
|
||||
if (ret < 0) {
|
||||
|
@ -279,14 +284,20 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
|
|||
*/
|
||||
qemu_iovec_init(&hd_qiov, qiov->niov);
|
||||
while (remaining_sectors > 0) {
|
||||
ret = bdrv_is_allocated_above(top->bs, base->bs, sector_num,
|
||||
remaining_sectors, &n);
|
||||
int64_t count;
|
||||
|
||||
ret = bdrv_is_allocated_above(top->bs, base->bs,
|
||||
sector_num * BDRV_SECTOR_SIZE,
|
||||
remaining_sectors * BDRV_SECTOR_SIZE,
|
||||
&count);
|
||||
if (ret < 0) {
|
||||
goto out1;
|
||||
}
|
||||
|
||||
assert(QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE));
|
||||
n = count >> BDRV_SECTOR_BITS;
|
||||
qemu_iovec_reset(&hd_qiov);
|
||||
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, n * BDRV_SECTOR_SIZE);
|
||||
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, count);
|
||||
|
||||
target = ret ? top : base;
|
||||
ret = bdrv_co_writev(target, sector_num, n, &hd_qiov);
|
||||
|
@ -296,7 +307,7 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
|
|||
|
||||
remaining_sectors -= n;
|
||||
sector_num += n;
|
||||
bytes_done += n * BDRV_SECTOR_SIZE;
|
||||
bytes_done += count;
|
||||
}
|
||||
|
||||
out1:
|
||||
|
|
|
@ -41,25 +41,24 @@ typedef struct StreamBlockJob {
|
|||
} StreamBlockJob;
|
||||
|
||||
static int coroutine_fn stream_populate(BlockBackend *blk,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
int64_t offset, uint64_t bytes,
|
||||
void *buf)
|
||||
{
|
||||
struct iovec iov = {
|
||||
.iov_base = buf,
|
||||
.iov_len = nb_sectors * BDRV_SECTOR_SIZE,
|
||||
.iov_len = bytes,
|
||||
};
|
||||
QEMUIOVector qiov;
|
||||
|
||||
assert(bytes < SIZE_MAX);
|
||||
qemu_iovec_init_external(&qiov, &iov, 1);
|
||||
|
||||
/* Copy-on-read the unallocated clusters */
|
||||
return blk_co_preadv(blk, sector_num * BDRV_SECTOR_SIZE, qiov.size, &qiov,
|
||||
BDRV_REQ_COPY_ON_READ);
|
||||
return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int ret;
|
||||
bool reached_end;
|
||||
} StreamCompleteData;
|
||||
|
||||
static void stream_complete(BlockJob *job, void *opaque)
|
||||
|
@ -70,7 +69,7 @@ static void stream_complete(BlockJob *job, void *opaque)
|
|||
BlockDriverState *base = s->base;
|
||||
Error *local_err = NULL;
|
||||
|
||||
if (!block_job_is_cancelled(&s->common) && data->reached_end &&
|
||||
if (!block_job_is_cancelled(&s->common) && bs->backing &&
|
||||
data->ret == 0) {
|
||||
const char *base_id = NULL, *base_fmt = NULL;
|
||||
if (base) {
|
||||
|
@ -108,12 +107,11 @@ static void coroutine_fn stream_run(void *opaque)
|
|||
BlockBackend *blk = s->common.blk;
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
BlockDriverState *base = s->base;
|
||||
int64_t sector_num = 0;
|
||||
int64_t end = -1;
|
||||
int64_t offset = 0;
|
||||
uint64_t delay_ns = 0;
|
||||
int error = 0;
|
||||
int ret = 0;
|
||||
int n = 0;
|
||||
int64_t n = 0; /* bytes */
|
||||
void *buf;
|
||||
|
||||
if (!bs->backing) {
|
||||
|
@ -126,7 +124,6 @@ static void coroutine_fn stream_run(void *opaque)
|
|||
goto out;
|
||||
}
|
||||
|
||||
end = s->common.len >> BDRV_SECTOR_BITS;
|
||||
buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE);
|
||||
|
||||
/* Turn on copy-on-read for the whole block device so that guest read
|
||||
|
@ -138,7 +135,7 @@ static void coroutine_fn stream_run(void *opaque)
|
|||
bdrv_enable_copy_on_read(bs);
|
||||
}
|
||||
|
||||
for (sector_num = 0; sector_num < end; sector_num += n) {
|
||||
for ( ; offset < s->common.len; offset += n) {
|
||||
bool copy;
|
||||
|
||||
/* Note that even when no rate limit is applied we need to yield
|
||||
|
@ -151,26 +148,25 @@ static void coroutine_fn stream_run(void *opaque)
|
|||
|
||||
copy = false;
|
||||
|
||||
ret = bdrv_is_allocated(bs, sector_num,
|
||||
STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n);
|
||||
ret = bdrv_is_allocated(bs, offset, STREAM_BUFFER_SIZE, &n);
|
||||
if (ret == 1) {
|
||||
/* Allocated in the top, no need to copy. */
|
||||
} else if (ret >= 0) {
|
||||
/* Copy if allocated in the intermediate images. Limit to the
|
||||
* known-unallocated area [sector_num, sector_num+n). */
|
||||
* known-unallocated area [offset, offset+n*BDRV_SECTOR_SIZE). */
|
||||
ret = bdrv_is_allocated_above(backing_bs(bs), base,
|
||||
sector_num, n, &n);
|
||||
offset, n, &n);
|
||||
|
||||
/* Finish early if end of backing file has been reached */
|
||||
if (ret == 0 && n == 0) {
|
||||
n = end - sector_num;
|
||||
n = s->common.len - offset;
|
||||
}
|
||||
|
||||
copy = (ret == 1);
|
||||
}
|
||||
trace_stream_one_iteration(s, sector_num, n, ret);
|
||||
trace_stream_one_iteration(s, offset, n, ret);
|
||||
if (copy) {
|
||||
ret = stream_populate(blk, sector_num, n, buf);
|
||||
ret = stream_populate(blk, offset, n, buf);
|
||||
}
|
||||
if (ret < 0) {
|
||||
BlockErrorAction action =
|
||||
|
@ -189,7 +185,7 @@ static void coroutine_fn stream_run(void *opaque)
|
|||
ret = 0;
|
||||
|
||||
/* Publish progress */
|
||||
s->common.offset += n * BDRV_SECTOR_SIZE;
|
||||
s->common.offset += n;
|
||||
if (copy && s->common.speed) {
|
||||
delay_ns = ratelimit_calculate_delay(&s->limit, n);
|
||||
}
|
||||
|
@ -208,7 +204,6 @@ out:
|
|||
/* Modify backing chain and close BDSes in main loop */
|
||||
data = g_malloc(sizeof(*data));
|
||||
data->ret = ret;
|
||||
data->reached_end = sector_num == end;
|
||||
block_job_defer_to_main_loop(&s->common, stream_complete, data);
|
||||
}
|
||||
|
||||
|
@ -220,7 +215,7 @@ static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|||
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
|
||||
return;
|
||||
}
|
||||
ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
|
||||
ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
|
||||
}
|
||||
|
||||
static const BlockJobDriver stream_job_driver = {
|
||||
|
|
|
@ -15,11 +15,11 @@ bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p off
|
|||
bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t cluster_offset, unsigned int cluster_bytes) "bs %p offset %"PRId64" bytes %u cluster_offset %"PRId64" cluster_bytes %u"
|
||||
|
||||
# block/stream.c
|
||||
stream_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d"
|
||||
stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d"
|
||||
stream_start(void *bs, void *base, void *s) "bs %p base %p s %p"
|
||||
|
||||
# block/commit.c
|
||||
commit_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d"
|
||||
commit_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d"
|
||||
commit_start(void *bs, void *base, void *top, void *s) "bs %p base %p top %p s %p"
|
||||
|
||||
# block/mirror.c
|
||||
|
@ -28,14 +28,14 @@ mirror_restart_iter(void *s, int64_t cnt) "s %p dirty count %"PRId64
|
|||
mirror_before_flush(void *s) "s %p"
|
||||
mirror_before_drain(void *s, int64_t cnt) "s %p dirty count %"PRId64
|
||||
mirror_before_sleep(void *s, int64_t cnt, int synced, uint64_t delay_ns) "s %p dirty count %"PRId64" synced %d delay %"PRIu64"ns"
|
||||
mirror_one_iteration(void *s, int64_t sector_num, int nb_sectors) "s %p sector_num %"PRId64" nb_sectors %d"
|
||||
mirror_iteration_done(void *s, int64_t sector_num, int nb_sectors, int ret) "s %p sector_num %"PRId64" nb_sectors %d ret %d"
|
||||
mirror_one_iteration(void *s, int64_t offset, uint64_t bytes) "s %p offset %" PRId64 " bytes %" PRIu64
|
||||
mirror_iteration_done(void *s, int64_t offset, uint64_t bytes, int ret) "s %p offset %" PRId64 " bytes %" PRIu64 " ret %d"
|
||||
mirror_yield(void *s, int64_t cnt, int buf_free_count, int in_flight) "s %p dirty count %"PRId64" free buffers %d in_flight %d"
|
||||
mirror_yield_in_flight(void *s, int64_t sector_num, int in_flight) "s %p sector_num %"PRId64" in_flight %d"
|
||||
mirror_yield_in_flight(void *s, int64_t offset, int in_flight) "s %p offset %" PRId64 " in_flight %d"
|
||||
|
||||
# block/backup.c
|
||||
backup_do_cow_enter(void *job, int64_t start, int64_t sector_num, int nb_sectors) "job %p start %"PRId64" sector_num %"PRId64" nb_sectors %d"
|
||||
backup_do_cow_return(void *job, int64_t sector_num, int nb_sectors, int ret) "job %p sector_num %"PRId64" nb_sectors %d ret %d"
|
||||
backup_do_cow_enter(void *job, int64_t start, int64_t offset, uint64_t bytes) "job %p start %" PRId64 " offset %" PRId64 " bytes %" PRIu64
|
||||
backup_do_cow_return(void *job, int64_t offset, uint64_t bytes, int ret) "job %p offset %" PRId64 " bytes %" PRIu64 " ret %d"
|
||||
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_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"
|
||||
|
|
|
@ -701,7 +701,7 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
|
|||
if (be32_to_cpu(footer->type) == VHD_FIXED) {
|
||||
*pnum = nb_sectors;
|
||||
*file = bs->file->bs;
|
||||
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID | BDRV_BLOCK_DATA |
|
||||
return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
|
||||
(sector_num << BDRV_SECTOR_BITS);
|
||||
}
|
||||
|
||||
|
|
2230
block/vvfat.c
2230
block/vvfat.c
File diff suppressed because it is too large
Load Diff
14
blockdev.c
14
blockdev.c
|
@ -50,6 +50,7 @@
|
|||
#include "qmp-commands.h"
|
||||
#include "block/trace.h"
|
||||
#include "sysemu/arch_init.h"
|
||||
#include "sysemu/qtest.h"
|
||||
#include "qemu/cutils.h"
|
||||
#include "qemu/help_option.h"
|
||||
#include "qemu/throttle-options.h"
|
||||
|
@ -798,6 +799,9 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||
const char *filename;
|
||||
Error *local_err = NULL;
|
||||
int i;
|
||||
const char *deprecated[] = {
|
||||
"serial", "trans", "secs", "heads", "cyls", "addr"
|
||||
};
|
||||
|
||||
/* Change legacy command line options into QMP ones */
|
||||
static const struct {
|
||||
|
@ -881,6 +885,16 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||
"update your scripts.\n");
|
||||
}
|
||||
|
||||
/* Other deprecated options */
|
||||
if (!qtest_enabled()) {
|
||||
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
|
||||
if (qemu_opt_get(legacy_opts, deprecated[i]) != NULL) {
|
||||
error_report("'%s' is deprecated, please use the corresponding "
|
||||
"option of '-device' instead", deprecated[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Media type */
|
||||
value = qemu_opt_get(legacy_opts, "media");
|
||||
if (value) {
|
||||
|
|
|
@ -132,9 +132,9 @@ typedef struct HDGeometry {
|
|||
* BDRV_BLOCK_EOF: the returned pnum covers through end of file for this layer
|
||||
*
|
||||
* Internal flag:
|
||||
* BDRV_BLOCK_RAW: used internally to indicate that the request was
|
||||
* answered by a passthrough driver such as raw and that the
|
||||
* block layer should recompute the answer from bs->file.
|
||||
* BDRV_BLOCK_RAW: for use by passthrough drivers, such as raw, to request
|
||||
* that the block layer recompute the answer from the returned
|
||||
* BDS; must be accompanied by just BDRV_BLOCK_OFFSET_VALID.
|
||||
*
|
||||
* If BDRV_BLOCK_OFFSET_VALID is set, bits 9-62 (BDRV_BLOCK_OFFSET_MASK)
|
||||
* represent the offset in the returned BDS that is allocated for the
|
||||
|
@ -427,10 +427,10 @@ int64_t bdrv_get_block_status_above(BlockDriverState *bs,
|
|||
int64_t sector_num,
|
||||
int nb_sectors, int *pnum,
|
||||
BlockDriverState **file);
|
||||
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||
int *pnum);
|
||||
int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes,
|
||||
int64_t *pnum);
|
||||
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
|
||||
int64_t sector_num, int nb_sectors, int *pnum);
|
||||
int64_t offset, int64_t bytes, int64_t *pnum);
|
||||
|
||||
bool bdrv_is_read_only(BlockDriverState *bs);
|
||||
bool bdrv_is_writable(BlockDriverState *bs);
|
||||
|
@ -475,10 +475,6 @@ const char *bdrv_get_device_or_node_name(const BlockDriverState *bs);
|
|||
int bdrv_get_flags(BlockDriverState *bs);
|
||||
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
|
||||
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs);
|
||||
void bdrv_round_sectors_to_clusters(BlockDriverState *bs,
|
||||
int64_t sector_num, int nb_sectors,
|
||||
int64_t *cluster_sector_num,
|
||||
int *cluster_nb_sectors);
|
||||
void bdrv_round_to_clusters(BlockDriverState *bs,
|
||||
int64_t offset, unsigned int bytes,
|
||||
int64_t *cluster_offset,
|
||||
|
|
|
@ -21,17 +21,16 @@
|
|||
#include "block/block_int.h"
|
||||
|
||||
typedef struct CowRequest {
|
||||
int64_t start;
|
||||
int64_t end;
|
||||
int64_t start_byte;
|
||||
int64_t end_byte;
|
||||
QLIST_ENTRY(CowRequest) list;
|
||||
CoQueue wait_queue; /* coroutines blocked on this request */
|
||||
} CowRequest;
|
||||
|
||||
void backup_wait_for_overlapping_requests(BlockJob *job, int64_t sector_num,
|
||||
int nb_sectors);
|
||||
void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
|
||||
uint64_t bytes);
|
||||
void backup_cow_request_begin(CowRequest *req, BlockJob *job,
|
||||
int64_t sector_num,
|
||||
int nb_sectors);
|
||||
int64_t offset, uint64_t bytes);
|
||||
void backup_cow_request_end(CowRequest *req);
|
||||
|
||||
void backup_do_checkpoint(BlockJob *job, Error **errp);
|
||||
|
|
|
@ -24,7 +24,8 @@ typedef struct {
|
|||
|
||||
/** Calculate and return delay for next request in ns
|
||||
*
|
||||
* Record that we sent @p n data units. If we may send more data units
|
||||
* Record that we sent @n data units (where @n matches the scale chosen
|
||||
* during ratelimit_set_speed). If we may send more data units
|
||||
* in the current time slice, return 0 (i.e. no delay). Otherwise
|
||||
* return the amount of time (in ns) until the start of the next time
|
||||
* slice that will permit sending the next chunk of data.
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
#define BLK_MIG_FLAG_PROGRESS 0x04
|
||||
#define BLK_MIG_FLAG_ZERO_BLOCK 0x08
|
||||
|
||||
#define MAX_IS_ALLOCATED_SEARCH 65536
|
||||
#define MAX_IS_ALLOCATED_SEARCH (65536 * BDRV_SECTOR_SIZE)
|
||||
|
||||
#define MAX_INFLIGHT_IO 512
|
||||
|
||||
|
@ -267,16 +267,20 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
|
|||
BlockBackend *bb = bmds->blk;
|
||||
BlkMigBlock *blk;
|
||||
int nr_sectors;
|
||||
int64_t count;
|
||||
|
||||
if (bmds->shared_base) {
|
||||
qemu_mutex_lock_iothread();
|
||||
aio_context_acquire(blk_get_aio_context(bb));
|
||||
/* Skip unallocated sectors; intentionally treats failure as
|
||||
* an allocated sector */
|
||||
/* Skip unallocated sectors; intentionally treats failure or
|
||||
* partial sector as an allocated sector */
|
||||
while (cur_sector < total_sectors &&
|
||||
!bdrv_is_allocated(blk_bs(bb), cur_sector,
|
||||
MAX_IS_ALLOCATED_SEARCH, &nr_sectors)) {
|
||||
cur_sector += nr_sectors;
|
||||
!bdrv_is_allocated(blk_bs(bb), cur_sector * BDRV_SECTOR_SIZE,
|
||||
MAX_IS_ALLOCATED_SEARCH, &count)) {
|
||||
if (count < BDRV_SECTOR_SIZE) {
|
||||
break;
|
||||
}
|
||||
cur_sector += count >> BDRV_SECTOR_BITS;
|
||||
}
|
||||
aio_context_release(blk_get_aio_context(bb));
|
||||
qemu_mutex_unlock_iothread();
|
||||
|
|
41
qemu-img.c
41
qemu-img.c
|
@ -464,7 +464,7 @@ static int img_create(int argc, char **argv)
|
|||
{"object", required_argument, 0, OPTION_OBJECT},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
c = getopt_long(argc, argv, ":F:b:f:he6o:q",
|
||||
c = getopt_long(argc, argv, ":F:b:f:ho:q",
|
||||
long_options, NULL);
|
||||
if (c == -1) {
|
||||
break;
|
||||
|
@ -488,14 +488,6 @@ static int img_create(int argc, char **argv)
|
|||
case 'f':
|
||||
fmt = optarg;
|
||||
break;
|
||||
case 'e':
|
||||
error_report("option -e is deprecated, please use \'-o "
|
||||
"encryption\' instead!");
|
||||
goto fail;
|
||||
case '6':
|
||||
error_report("option -6 is deprecated, please use \'-o "
|
||||
"compat6\' instead!");
|
||||
goto fail;
|
||||
case 'o':
|
||||
if (!is_valid_option_list(optarg)) {
|
||||
error_report("Invalid option list: %s", optarg);
|
||||
|
@ -1516,12 +1508,16 @@ static int img_compare(int argc, char **argv)
|
|||
}
|
||||
|
||||
for (;;) {
|
||||
int64_t count;
|
||||
|
||||
nb_sectors = sectors_to_process(total_sectors_over, sector_num);
|
||||
if (nb_sectors <= 0) {
|
||||
break;
|
||||
}
|
||||
ret = bdrv_is_allocated_above(blk_bs(blk_over), NULL, sector_num,
|
||||
nb_sectors, &pnum);
|
||||
ret = bdrv_is_allocated_above(blk_bs(blk_over), NULL,
|
||||
sector_num * BDRV_SECTOR_SIZE,
|
||||
nb_sectors * BDRV_SECTOR_SIZE,
|
||||
&count);
|
||||
if (ret < 0) {
|
||||
ret = 3;
|
||||
error_report("Sector allocation test failed for %s",
|
||||
|
@ -1529,7 +1525,10 @@ static int img_compare(int argc, char **argv)
|
|||
goto out;
|
||||
|
||||
}
|
||||
nb_sectors = pnum;
|
||||
/* TODO relax this once bdrv_is_allocated_above does not enforce
|
||||
* sector alignment */
|
||||
assert(QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE));
|
||||
nb_sectors = count >> BDRV_SECTOR_BITS;
|
||||
if (ret) {
|
||||
ret = check_empty_sectors(blk_over, sector_num, nb_sectors,
|
||||
filename_over, buf1, quiet);
|
||||
|
@ -1985,7 +1984,7 @@ static int img_convert(int argc, char **argv)
|
|||
{"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
c = getopt_long(argc, argv, ":hf:O:B:ce6o:s:l:S:pt:T:qnm:WU",
|
||||
c = getopt_long(argc, argv, ":hf:O:B:co:s:l:S:pt:T:qnm:WU",
|
||||
long_options, NULL);
|
||||
if (c == -1) {
|
||||
break;
|
||||
|
@ -2012,14 +2011,6 @@ static int img_convert(int argc, char **argv)
|
|||
case 'c':
|
||||
s.compressed = true;
|
||||
break;
|
||||
case 'e':
|
||||
error_report("option -e is deprecated, please use \'-o "
|
||||
"encryption\' instead!");
|
||||
goto fail_getopt;
|
||||
case '6':
|
||||
error_report("option -6 is deprecated, please use \'-o "
|
||||
"compat6\' instead!");
|
||||
goto fail_getopt;
|
||||
case 'o':
|
||||
if (!is_valid_option_list(optarg)) {
|
||||
error_report("Invalid option list: %s", optarg);
|
||||
|
@ -3274,6 +3265,7 @@ static int img_rebase(int argc, char **argv)
|
|||
int64_t new_backing_num_sectors = 0;
|
||||
uint64_t sector;
|
||||
int n;
|
||||
int64_t count;
|
||||
float local_progress = 0;
|
||||
|
||||
buf_old = blk_blockalign(blk, IO_BUF_SIZE);
|
||||
|
@ -3321,12 +3313,17 @@ static int img_rebase(int argc, char **argv)
|
|||
}
|
||||
|
||||
/* If the cluster is allocated, we don't need to take action */
|
||||
ret = bdrv_is_allocated(bs, sector, n, &n);
|
||||
ret = bdrv_is_allocated(bs, sector << BDRV_SECTOR_BITS,
|
||||
n << BDRV_SECTOR_BITS, &count);
|
||||
if (ret < 0) {
|
||||
error_report("error while reading image metadata: %s",
|
||||
strerror(-ret));
|
||||
goto out;
|
||||
}
|
||||
/* TODO relax this once bdrv_is_allocated does not enforce
|
||||
* sector alignment */
|
||||
assert(QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE));
|
||||
n = count >> BDRV_SECTOR_BITS;
|
||||
if (ret) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -1760,12 +1760,12 @@ out:
|
|||
static int alloc_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
BlockDriverState *bs = blk_bs(blk);
|
||||
int64_t offset, sector_num, nb_sectors, remaining, count;
|
||||
int64_t offset, start, remaining, count;
|
||||
char s1[64];
|
||||
int num, ret;
|
||||
int64_t sum_alloc;
|
||||
int ret;
|
||||
int64_t num, sum_alloc;
|
||||
|
||||
offset = cvtnum(argv[1]);
|
||||
start = offset = cvtnum(argv[1]);
|
||||
if (offset < 0) {
|
||||
print_cvtnum_err(offset, argv[1]);
|
||||
return 0;
|
||||
|
@ -1793,32 +1793,30 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv)
|
|||
count);
|
||||
return 0;
|
||||
}
|
||||
nb_sectors = count >> BDRV_SECTOR_BITS;
|
||||
|
||||
remaining = nb_sectors;
|
||||
remaining = count;
|
||||
sum_alloc = 0;
|
||||
sector_num = offset >> 9;
|
||||
while (remaining) {
|
||||
ret = bdrv_is_allocated(bs, sector_num, remaining, &num);
|
||||
ret = bdrv_is_allocated(bs, offset, remaining, &num);
|
||||
if (ret < 0) {
|
||||
printf("is_allocated failed: %s\n", strerror(-ret));
|
||||
return 0;
|
||||
}
|
||||
sector_num += num;
|
||||
offset += num;
|
||||
remaining -= num;
|
||||
if (ret) {
|
||||
sum_alloc += num;
|
||||
}
|
||||
if (num == 0) {
|
||||
nb_sectors -= remaining;
|
||||
count -= remaining;
|
||||
remaining = 0;
|
||||
}
|
||||
}
|
||||
|
||||
cvtstr(offset, s1, sizeof(s1));
|
||||
cvtstr(start, s1, sizeof(s1));
|
||||
|
||||
printf("%"PRId64"/%"PRId64" bytes allocated at offset %s\n",
|
||||
sum_alloc << BDRV_SECTOR_BITS, nb_sectors << BDRV_SECTOR_BITS, s1);
|
||||
sum_alloc, count, s1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1833,14 +1831,15 @@ static const cmdinfo_t alloc_cmd = {
|
|||
};
|
||||
|
||||
|
||||
static int map_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||
int64_t nb_sectors, int64_t *pnum)
|
||||
static int map_is_allocated(BlockDriverState *bs, int64_t offset,
|
||||
int64_t bytes, int64_t *pnum)
|
||||
{
|
||||
int num, num_checked;
|
||||
int64_t num;
|
||||
int num_checked;
|
||||
int ret, firstret;
|
||||
|
||||
num_checked = MIN(nb_sectors, INT_MAX);
|
||||
ret = bdrv_is_allocated(bs, sector_num, num_checked, &num);
|
||||
num_checked = MIN(bytes, BDRV_REQUEST_MAX_BYTES);
|
||||
ret = bdrv_is_allocated(bs, offset, num_checked, &num);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -1848,12 +1847,12 @@ static int map_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
|||
firstret = ret;
|
||||
*pnum = num;
|
||||
|
||||
while (nb_sectors > 0 && ret == firstret) {
|
||||
sector_num += num;
|
||||
nb_sectors -= num;
|
||||
while (bytes > 0 && ret == firstret) {
|
||||
offset += num;
|
||||
bytes -= num;
|
||||
|
||||
num_checked = MIN(nb_sectors, INT_MAX);
|
||||
ret = bdrv_is_allocated(bs, sector_num, num_checked, &num);
|
||||
num_checked = MIN(bytes, BDRV_REQUEST_MAX_BYTES);
|
||||
ret = bdrv_is_allocated(bs, offset, num_checked, &num);
|
||||
if (ret == firstret && num) {
|
||||
*pnum += num;
|
||||
} else {
|
||||
|
@ -1866,25 +1865,21 @@ static int map_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
|||
|
||||
static int map_f(BlockBackend *blk, int argc, char **argv)
|
||||
{
|
||||
int64_t offset;
|
||||
int64_t nb_sectors, total_sectors;
|
||||
int64_t offset, bytes;
|
||||
char s1[64], s2[64];
|
||||
int64_t num;
|
||||
int ret;
|
||||
const char *retstr;
|
||||
|
||||
offset = 0;
|
||||
total_sectors = blk_nb_sectors(blk);
|
||||
if (total_sectors < 0) {
|
||||
error_report("Failed to query image length: %s",
|
||||
strerror(-total_sectors));
|
||||
bytes = blk_getlength(blk);
|
||||
if (bytes < 0) {
|
||||
error_report("Failed to query image length: %s", strerror(-bytes));
|
||||
return 0;
|
||||
}
|
||||
|
||||
nb_sectors = total_sectors;
|
||||
|
||||
do {
|
||||
ret = map_is_allocated(blk_bs(blk), offset, nb_sectors, &num);
|
||||
while (bytes) {
|
||||
ret = map_is_allocated(blk_bs(blk), offset, bytes, &num);
|
||||
if (ret < 0) {
|
||||
error_report("Failed to get allocation status: %s", strerror(-ret));
|
||||
return 0;
|
||||
|
@ -1894,15 +1889,14 @@ static int map_f(BlockBackend *blk, int argc, char **argv)
|
|||
}
|
||||
|
||||
retstr = ret ? " allocated" : "not allocated";
|
||||
cvtstr(num << BDRV_SECTOR_BITS, s1, sizeof(s1));
|
||||
cvtstr(offset << BDRV_SECTOR_BITS, s2, sizeof(s2));
|
||||
cvtstr(num, s1, sizeof(s1));
|
||||
cvtstr(offset, s2, sizeof(s2));
|
||||
printf("%s (0x%" PRIx64 ") bytes %s at offset %s (0x%" PRIx64 ")\n",
|
||||
s1, num << BDRV_SECTOR_BITS, retstr,
|
||||
s2, offset << BDRV_SECTOR_BITS);
|
||||
s1, num, retstr, s2, offset);
|
||||
|
||||
offset += num;
|
||||
nb_sectors -= num;
|
||||
} while (offset < total_sectors);
|
||||
bytes -= num;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -230,13 +230,14 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
|
|||
qemu_opts_reset(&empty_opts);
|
||||
|
||||
if (optind == argc - 1) {
|
||||
return openfile(argv[optind], flags, writethrough, force_share, opts);
|
||||
openfile(argv[optind], flags, writethrough, force_share, opts);
|
||||
} else if (optind == argc) {
|
||||
return openfile(NULL, flags, writethrough, force_share, opts);
|
||||
openfile(NULL, flags, writethrough, force_share, opts);
|
||||
} else {
|
||||
QDECREF(opts);
|
||||
return qemuio_command_usage(&open_cmd);
|
||||
qemuio_command_usage(&open_cmd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int quit_f(BlockBackend *blk, int argc, char **argv)
|
||||
|
|
|
@ -818,6 +818,8 @@ of available connectors of a given interface type.
|
|||
This option defines the type of the media: disk or cdrom.
|
||||
@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}]
|
||||
These options have the same definition as they have in @option{-hdachs}.
|
||||
These parameters are deprecated, use the corresponding parameters
|
||||
of @code{-device} instead.
|
||||
@item snapshot=@var{snapshot}
|
||||
@var{snapshot} is "on" or "off" and controls snapshot mode for the given drive
|
||||
(see @option{-snapshot}).
|
||||
|
@ -852,9 +854,12 @@ Specify which disk @var{format} will be used rather than detecting
|
|||
the format. Can be used to specify format=raw to avoid interpreting
|
||||
an untrusted format header.
|
||||
@item serial=@var{serial}
|
||||
This option specifies the serial number to assign to the device.
|
||||
This option specifies the serial number to assign to the device. This
|
||||
parameter is deprecated, use the corresponding parameter of @code{-device}
|
||||
instead.
|
||||
@item addr=@var{addr}
|
||||
Specify the controller's PCI address (if=virtio only).
|
||||
Specify the controller's PCI address (if=virtio only). This parameter is
|
||||
deprecated, use the corresponding parameter of @code{-device} instead.
|
||||
@item werror=@var{action},rerror=@var{action}
|
||||
Specify which @var{action} to take on write and read errors. Valid actions are:
|
||||
"ignore" (ignore the error and try to continue), "stop" (pause QEMU),
|
||||
|
|
|
@ -21,6 +21,7 @@ Format specific information:
|
|||
refcount bits: 16
|
||||
corrupt: true
|
||||
can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write
|
||||
no file open, try 'help open'
|
||||
read 512/512 bytes at offset 0
|
||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
QA output created by 114
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=67108864
|
||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 backing_file=TEST_DIR/t.IMGFMT.base
|
||||
image: TEST_DIR/t.IMGFMT
|
||||
file format: IMGFMT
|
||||
virtual size: 64M (67108864 bytes)
|
||||
|
@ -8,6 +8,7 @@ cluster_size: 65536
|
|||
backing file: TEST_DIR/t.IMGFMT.base
|
||||
backing file format: foo
|
||||
can't open device TEST_DIR/t.qcow2: Could not open backing file: Unknown driver 'foo'
|
||||
no file open, try 'help open'
|
||||
read 4096/4096 bytes at offset 0
|
||||
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
*** done
|
||||
|
|
|
@ -33,10 +33,12 @@ Is another process using the image?
|
|||
_qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512
|
||||
can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
no file open, try 'help open'
|
||||
|
||||
_qemu_io_wrapper -c open -r TEST_DIR/t.qcow2 -c read 0 512
|
||||
can't open device TEST_DIR/t.qcow2: Failed to get shared "write" lock
|
||||
Is another process using the image?
|
||||
no file open, try 'help open'
|
||||
|
||||
_qemu_img_wrapper info TEST_DIR/t.qcow2
|
||||
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
|
||||
|
@ -99,6 +101,7 @@ _qemu_io_wrapper -U -r -c read 0 512 TEST_DIR/t.qcow2
|
|||
|
||||
_qemu_io_wrapper -c open -U TEST_DIR/t.qcow2 -c read 0 512
|
||||
can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
|
||||
no file open, try 'help open'
|
||||
|
||||
_qemu_io_wrapper -c open -r -U TEST_DIR/t.qcow2 -c read 0 512
|
||||
|
||||
|
@ -166,6 +169,7 @@ _qemu_io_wrapper -r -c read 0 512 TEST_DIR/t.qcow2
|
|||
_qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512
|
||||
can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
|
||||
Is another process using the image?
|
||||
no file open, try 'help open'
|
||||
|
||||
_qemu_io_wrapper -c open -r TEST_DIR/t.qcow2 -c read 0 512
|
||||
|
||||
|
@ -214,6 +218,7 @@ _qemu_io_wrapper -U -r -c read 0 512 TEST_DIR/t.qcow2
|
|||
|
||||
_qemu_io_wrapper -c open -U TEST_DIR/t.qcow2 -c read 0 512
|
||||
can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
|
||||
no file open, try 'help open'
|
||||
|
||||
_qemu_io_wrapper -c open -r -U TEST_DIR/t.qcow2 -c read 0 512
|
||||
|
||||
|
@ -313,6 +318,7 @@ _qemu_io_wrapper -U -r -c read 0 512 TEST_DIR/t.qcow2
|
|||
|
||||
_qemu_io_wrapper -c open -U TEST_DIR/t.qcow2 -c read 0 512
|
||||
can't open device TEST_DIR/t.qcow2: force-share=on can only be used with read-only images
|
||||
no file open, try 'help open'
|
||||
|
||||
_qemu_io_wrapper -c open -r -U TEST_DIR/t.qcow2 -c read 0 512
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ _supported_proto file
|
|||
CLUSTER_SIZE=1M
|
||||
size=128M
|
||||
options=driver=blkdebug,image.driver=qcow2
|
||||
nested_opts=image.file.driver=file,image.file.filename=$TEST_IMG
|
||||
|
||||
echo
|
||||
echo "== setting up files =="
|
||||
|
@ -106,6 +107,8 @@ function verify_io()
|
|||
}
|
||||
|
||||
verify_io | $QEMU_IO -r "$TEST_IMG" | _filter_qemu_io
|
||||
$QEMU_IMG map --image-opts "$options,$nested_opts,align=4k" \
|
||||
| _filter_qemu_img_map
|
||||
|
||||
_check_test_img
|
||||
|
||||
|
|
|
@ -45,5 +45,10 @@ read 30408704/30408704 bytes at offset 80740352
|
|||
29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
read 23068672/23068672 bytes at offset 111149056
|
||||
22 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||
Offset Length File
|
||||
0 0x800000 TEST_DIR/t.IMGFMT
|
||||
0x900000 0x2400000 TEST_DIR/t.IMGFMT
|
||||
0x3c00000 0x1100000 TEST_DIR/t.IMGFMT
|
||||
0x6a00000 0x1600000 TEST_DIR/t.IMGFMT
|
||||
No errors were found on the image.
|
||||
*** done
|
||||
|
|
Loading…
Reference in New Issue