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:
Peter Maydell 2017-07-10 14:06:49 +01:00
commit 94c56652b9
25 changed files with 1622 additions and 1506 deletions

View File

@ -39,7 +39,7 @@ typedef struct BackupBlockJob {
BlockdevOnError on_source_error; BlockdevOnError on_source_error;
BlockdevOnError on_target_error; BlockdevOnError on_target_error;
CoRwlock flush_rwlock; CoRwlock flush_rwlock;
uint64_t sectors_read; uint64_t bytes_read;
unsigned long *done_bitmap; unsigned long *done_bitmap;
int64_t cluster_size; int64_t cluster_size;
bool compress; bool compress;
@ -47,12 +47,6 @@ typedef struct BackupBlockJob {
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,
@ -64,7 +58,7 @@ static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
do { do {
retry = false; retry = false;
QLIST_FOREACH(req, &job->inflight_reqs, list) { 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); qemu_co_queue_wait(&req->wait_queue, NULL);
retry = true; retry = true;
break; break;
@ -77,8 +71,8 @@ static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
static void cow_request_begin(CowRequest *req, BackupBlockJob *job, 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->start_byte = start;
req->end = end; req->end_byte = end;
qemu_co_queue_init(&req->wait_queue); qemu_co_queue_init(&req->wait_queue);
QLIST_INSERT_HEAD(&job->inflight_reqs, req, list); 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, 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 *error_is_read,
bool is_write_notifier) bool is_write_notifier)
{ {
@ -101,41 +95,37 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
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; /* bytes */
int64_t start, end; int n; /* bytes */
int n;
qemu_co_rwlock_rdlock(&job->flush_rwlock); qemu_co_rwlock_rdlock(&job->flush_rwlock);
start = sector_num / sectors_per_cluster; start = QEMU_ALIGN_DOWN(offset, job->cluster_size);
end = DIV_ROUND_UP(sector_num + nb_sectors, sectors_per_cluster); 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); wait_for_overlapping_requests(job, start, end);
cow_request_begin(&cow_request, job, start, end); cow_request_begin(&cow_request, job, start, end);
for (; start < end; start++) { for (; start < end; start += job->cluster_size) {
if (test_bit(start, job->done_bitmap)) { if (test_bit(start / job->cluster_size, job->done_bitmap)) {
trace_backup_do_cow_skip(job, start); trace_backup_do_cow_skip(job, start);
continue; /* already copied */ continue; /* already copied */
} }
trace_backup_do_cow_process(job, start); trace_backup_do_cow_process(job, start);
n = MIN(sectors_per_cluster, n = MIN(job->cluster_size, job->common.len - start);
job->common.len / BDRV_SECTOR_SIZE -
start * sectors_per_cluster);
if (!bounce_buffer) { if (!bounce_buffer) {
bounce_buffer = blk_blockalign(blk, job->cluster_size); bounce_buffer = blk_blockalign(blk, job->cluster_size);
} }
iov.iov_base = bounce_buffer; iov.iov_base = bounce_buffer;
iov.iov_len = n * BDRV_SECTOR_SIZE; iov.iov_len = n;
qemu_iovec_init_external(&bounce_qiov, &iov, 1); qemu_iovec_init_external(&bounce_qiov, &iov, 1);
ret = blk_co_preadv(blk, start * job->cluster_size, ret = blk_co_preadv(blk, start, bounce_qiov.size, &bounce_qiov,
bounce_qiov.size, &bounce_qiov,
is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0); is_write_notifier ? BDRV_REQ_NO_SERIALISING : 0);
if (ret < 0) { if (ret < 0) {
trace_backup_do_cow_read_fail(job, start, ret); 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)) { 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); bounce_qiov.size, BDRV_REQ_MAY_UNMAP);
} else { } else {
ret = blk_co_pwritev(job->target, start * job->cluster_size, ret = blk_co_pwritev(job->target, start,
bounce_qiov.size, &bounce_qiov, bounce_qiov.size, &bounce_qiov,
job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0);
} }
@ -161,13 +151,13 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
goto out; 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 /* Publish progress, guest I/O counts as progress too. Note that the
* offset field is an opaque progress value, it is not a disk offset. * offset field is an opaque progress value, it is not a disk offset.
*/ */
job->sectors_read += n; job->bytes_read += n;
job->common.offset += n * BDRV_SECTOR_SIZE; job->common.offset += n;
} }
out: out:
@ -177,7 +167,7 @@ out:
cow_request_end(&cow_request); 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); 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); BackupBlockJob *job = container_of(notifier, BackupBlockJob, before_write);
BdrvTrackedRequest *req = opaque; 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->bs == blk_bs(job->common.blk));
assert((req->offset & (BDRV_SECTOR_SIZE - 1)) == 0); assert(QEMU_IS_ALIGNED(req->offset, BDRV_SECTOR_SIZE));
assert((req->bytes & (BDRV_SECTOR_SIZE - 1)) == 0); 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) 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"); error_setg(errp, QERR_INVALID_PARAMETER, "speed");
return; 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) 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); bitmap_zero(backup_job->done_bitmap, len);
} }
void backup_wait_for_overlapping_requests(BlockJob *job, int64_t sector_num, void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
int nb_sectors) uint64_t bytes)
{ {
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
int64_t sectors_per_cluster = cluster_size_sectors(backup_job);
int64_t start, end; int64_t start, end;
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
start = sector_num / sectors_per_cluster; start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
end = DIV_ROUND_UP(sector_num + nb_sectors, sectors_per_cluster); end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
wait_for_overlapping_requests(backup_job, start, end); wait_for_overlapping_requests(backup_job, start, end);
} }
void backup_cow_request_begin(CowRequest *req, BlockJob *job, void backup_cow_request_begin(CowRequest *req, BlockJob *job,
int64_t sector_num, int64_t offset, uint64_t bytes)
int nb_sectors)
{ {
BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common);
int64_t sectors_per_cluster = cluster_size_sectors(backup_job);
int64_t start, end; int64_t start, end;
assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP); assert(job->driver->job_type == BLOCK_JOB_TYPE_BACKUP);
start = sector_num / sectors_per_cluster; start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size);
end = DIV_ROUND_UP(sector_num + nb_sectors, sectors_per_cluster); end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size);
cow_request_begin(req, backup_job, start, end); 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) { if (job->common.speed) {
uint64_t delay_ns = ratelimit_calculate_delay(&job->limit, uint64_t delay_ns = ratelimit_calculate_delay(&job->limit,
job->sectors_read); job->bytes_read);
job->sectors_read = 0; job->bytes_read = 0;
block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, delay_ns); block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, delay_ns);
} else { } else {
block_job_sleep_ns(&job->common, QEMU_CLOCK_REALTIME, 0); 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 ret = 0;
int clusters_per_iter; int clusters_per_iter;
uint32_t granularity; uint32_t granularity;
int64_t sector; int64_t offset;
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);
BdrvDirtyBitmapIter *dbi; BdrvDirtyBitmapIter *dbi;
granularity = bdrv_dirty_bitmap_granularity(job->sync_bitmap); 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); dbi = bdrv_dirty_iter_new(job->sync_bitmap, 0);
/* Find the next dirty sector(s) */ /* Find the next dirty sector(s) */
while ((sector = bdrv_dirty_iter_next(dbi)) != -1) { while ((offset = bdrv_dirty_iter_next(dbi) * BDRV_SECTOR_SIZE) >= 0) {
cluster = sector / sectors_per_cluster; cluster = offset / job->cluster_size;
/* 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) {
@ -405,8 +389,8 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job)
if (yield_and_check(job)) { if (yield_and_check(job)) {
goto out; goto out;
} }
ret = backup_do_cow(job, cluster * sectors_per_cluster, ret = backup_do_cow(job, cluster * job->cluster_size,
sectors_per_cluster, &error_is_read, job->cluster_size, &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) ==
@ -419,7 +403,8 @@ 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 < job->cluster_size) { 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; last_cluster = cluster - 1;
@ -441,17 +426,14 @@ static void coroutine_fn backup_run(void *opaque)
BackupBlockJob *job = opaque; BackupBlockJob *job = opaque;
BackupCompleteData *data; BackupCompleteData *data;
BlockDriverState *bs = blk_bs(job->common.blk); BlockDriverState *bs = blk_bs(job->common.blk);
int64_t start, end; int64_t offset;
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; job->done_bitmap = bitmap_new(DIV_ROUND_UP(job->common.len,
end = DIV_ROUND_UP(job->common.len, job->cluster_size); job->cluster_size));
job->done_bitmap = bitmap_new(end);
job->before_write.notify = backup_before_write_notify; job->before_write.notify = backup_before_write_notify;
bdrv_add_before_write_notifier(bs, &job->before_write); 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); ret = backup_run_incremental(job);
} else { } else {
/* Both FULL and TOP SYNC_MODE's require copying.. */ /* 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; bool error_is_read;
int alloced = 0; int alloced = 0;
@ -475,12 +458,13 @@ static void coroutine_fn backup_run(void *opaque)
} }
if (job->sync_mode == MIRROR_SYNC_MODE_TOP) { 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 /* Check to see if these blocks are already in the
* backing file. */ * backing file. */
for (i = 0; i < sectors_per_cluster;) { for (i = 0; i < job->cluster_size;) {
/* 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.
@ -488,9 +472,8 @@ static void coroutine_fn backup_run(void *opaque)
* backup cluster length. We end up copying more than * backup cluster length. We end up copying more than
* 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, offset + i,
start * sectors_per_cluster + i, job->cluster_size - i, &n);
sectors_per_cluster - i, &n);
i += n; i += n;
if (alloced || n == 0) { if (alloced || n == 0) {
@ -508,9 +491,8 @@ static void coroutine_fn backup_run(void *opaque)
if (alloced < 0) { if (alloced < 0) {
ret = alloced; ret = alloced;
} else { } else {
ret = backup_do_cow(job, start * sectors_per_cluster, ret = backup_do_cow(job, offset, job->cluster_size,
sectors_per_cluster, &error_is_read, &error_is_read, false);
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 */
@ -519,7 +501,7 @@ static void coroutine_fn backup_run(void *opaque)
if (action == BLOCK_ERROR_ACTION_REPORT) { if (action == BLOCK_ERROR_ACTION_REPORT) {
break; break;
} else { } else {
start--; offset -= job->cluster_size;
continue; continue;
} }
} }

View File

@ -641,6 +641,16 @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
return bdrv_co_pdiscard(bs->file->bs, offset, bytes); 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) static void blkdebug_close(BlockDriverState *bs)
{ {
BDRVBlkdebugState *s = bs->opaque; BDRVBlkdebugState *s = bs->opaque;
@ -915,6 +925,7 @@ static BlockDriver bdrv_blkdebug = {
.bdrv_co_flush_to_disk = blkdebug_co_flush, .bdrv_co_flush_to_disk = blkdebug_co_flush,
.bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes, .bdrv_co_pwrite_zeroes = blkdebug_co_pwrite_zeroes,
.bdrv_co_pdiscard = blkdebug_co_pdiscard, .bdrv_co_pdiscard = blkdebug_co_pdiscard,
.bdrv_co_get_block_status = blkdebug_co_get_block_status,
.bdrv_debug_event = blkdebug_debug_event, .bdrv_debug_event = blkdebug_debug_event,
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint, .bdrv_debug_breakpoint = blkdebug_debug_breakpoint,

View File

@ -47,26 +47,25 @@ typedef struct CommitBlockJob {
} CommitBlockJob; } CommitBlockJob;
static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, 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) void *buf)
{ {
int ret = 0; int ret = 0;
QEMUIOVector qiov; QEMUIOVector qiov;
struct iovec iov = { struct iovec iov = {
.iov_base = buf, .iov_base = buf,
.iov_len = nb_sectors * BDRV_SECTOR_SIZE, .iov_len = bytes,
}; };
assert(bytes < SIZE_MAX);
qemu_iovec_init_external(&qiov, &iov, 1); qemu_iovec_init_external(&qiov, &iov, 1);
ret = blk_co_preadv(bs, sector_num * BDRV_SECTOR_SIZE, ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0);
qiov.size, &qiov, 0);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
ret = blk_co_pwritev(base, sector_num * BDRV_SECTOR_SIZE, ret = blk_co_pwritev(base, offset, qiov.size, &qiov, 0);
qiov.size, &qiov, 0);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -144,17 +143,16 @@ static void coroutine_fn commit_run(void *opaque)
{ {
CommitBlockJob *s = opaque; CommitBlockJob *s = opaque;
CommitCompleteData *data; CommitCompleteData *data;
int64_t sector_num, end; int64_t offset;
uint64_t delay_ns = 0; uint64_t delay_ns = 0;
int ret = 0; int ret = 0;
int n = 0; int64_t n = 0; /* bytes */
void *buf = NULL; void *buf = NULL;
int bytes_written = 0; int bytes_written = 0;
int64_t base_len; int64_t base_len;
ret = s->common.len = blk_getlength(s->top); ret = s->common.len = blk_getlength(s->top);
if (s->common.len < 0) { if (s->common.len < 0) {
goto out; 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); 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; bool copy;
/* Note that even when no rate limit is applied we need to yield /* 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 */ /* Copy if allocated above the base */
ret = bdrv_is_allocated_above(blk_bs(s->top), blk_bs(s->base), ret = bdrv_is_allocated_above(blk_bs(s->top), blk_bs(s->base),
sector_num, offset, COMMIT_BUFFER_SIZE, &n);
COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE,
&n);
copy = (ret == 1); copy = (ret == 1);
trace_commit_one_iteration(s, sector_num, n, ret); trace_commit_one_iteration(s, offset, n, ret);
if (copy) { if (copy) {
ret = commit_populate(s->top, s->base, sector_num, n, buf); ret = commit_populate(s->top, s->base, offset, n, buf);
bytes_written += n * BDRV_SECTOR_SIZE; bytes_written += n;
} }
if (ret < 0) { if (ret < 0) {
BlockErrorAction action = BlockErrorAction action =
@ -206,7 +201,7 @@ static void coroutine_fn commit_run(void *opaque)
} }
} }
/* Publish progress */ /* Publish progress */
s->common.offset += n * BDRV_SECTOR_SIZE; s->common.offset += n;
if (copy && s->common.speed) { if (copy && s->common.speed) {
delay_ns = ratelimit_calculate_delay(&s->limit, n); 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"); error_setg(errp, QERR_INVALID_PARAMETER, "speed");
return; 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 = { static const BlockJobDriver commit_job_driver = {
@ -253,7 +248,7 @@ static int64_t coroutine_fn bdrv_commit_top_get_block_status(
{ {
*pnum = nb_sectors; *pnum = nb_sectors;
*file = bs->backing->bs; *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); (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 */ /* commit COW file into the raw image */
int bdrv_commit(BlockDriverState *bs) int bdrv_commit(BlockDriverState *bs)
@ -453,8 +448,9 @@ int bdrv_commit(BlockDriverState *bs)
BlockDriverState *backing_file_bs = NULL; BlockDriverState *backing_file_bs = NULL;
BlockDriverState *commit_top_bs = NULL; BlockDriverState *commit_top_bs = NULL;
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
int64_t sector, total_sectors, length, backing_length; int64_t offset, length, backing_length;
int n, ro, open_flags; int ro, open_flags;
int64_t n;
int ret = 0; int ret = 0;
uint8_t *buf = NULL; uint8_t *buf = NULL;
Error *local_err = 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 /* blk_try_blockalign() for src will choose an alignment that works for
* backing as well, so no need to compare the alignment manually. */ * 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) { if (buf == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
goto ro_cleanup; goto ro_cleanup;
} }
for (sector = 0; sector < total_sectors; sector += n) { for (offset = 0; offset < length; offset += n) {
ret = bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n); ret = bdrv_is_allocated(bs, offset, COMMIT_BUF_SIZE, &n);
if (ret < 0) { if (ret < 0) {
goto ro_cleanup; goto ro_cleanup;
} }
if (ret) { if (ret) {
ret = blk_pread(src, sector * BDRV_SECTOR_SIZE, buf, ret = blk_pread(src, offset, buf, n);
n * BDRV_SECTOR_SIZE);
if (ret < 0) { if (ret < 0) {
goto ro_cleanup; goto ro_cleanup;
} }
ret = blk_pwrite(backing, sector * BDRV_SECTOR_SIZE, buf, ret = blk_pwrite(backing, offset, buf, n, 0);
n * BDRV_SECTOR_SIZE, 0);
if (ret < 0) { if (ret < 0) {
goto ro_cleanup; goto ro_cleanup;
} }

View File

@ -418,27 +418,6 @@ static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes); 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 * 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) { if (flags & BDRV_REQ_COPY_ON_READ) {
int64_t start_sector = offset >> BDRV_SECTOR_BITS; /* TODO: Simplify further once bdrv_is_allocated no longer
int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE); * requires sector alignment */
unsigned int nb_sectors = end_sector - start_sector; int64_t start = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE);
int pnum; 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) { if (ret < 0) {
goto out; goto out;
} }
if (!ret || pnum != nb_sectors) { if (!ret || pnum != end - start) {
ret = bdrv_co_do_copy_on_readv(child, offset, bytes, qiov); ret = bdrv_co_do_copy_on_readv(child, offset, bytes, qiov);
goto out; goto out;
} }
@ -1734,6 +1714,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
int64_t n; int64_t n;
int64_t ret, ret2; int64_t ret, ret2;
*file = NULL;
total_sectors = bdrv_nb_sectors(bs); total_sectors = bdrv_nb_sectors(bs);
if (total_sectors < 0) { if (total_sectors < 0) {
return total_sectors; return total_sectors;
@ -1757,11 +1738,11 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
} }
if (bs->drv->protocol_name) { if (bs->drv->protocol_name) {
ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE); ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE);
*file = bs;
} }
return ret; return ret;
} }
*file = NULL;
bdrv_inc_in_flight(bs); bdrv_inc_in_flight(bs);
ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum, ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum,
file); file);
@ -1771,7 +1752,7 @@ static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
} }
if (ret & BDRV_BLOCK_RAW) { 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, ret = bdrv_co_get_block_status(*file, ret >> BDRV_SECTOR_BITS,
*pnum, pnum, file); *pnum, pnum, file);
goto out; goto out;
@ -1920,59 +1901,72 @@ int64_t bdrv_get_block_status(BlockDriverState *bs,
sector_num, nb_sectors, pnum, file); sector_num, nb_sectors, pnum, file);
} }
int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t offset,
int nb_sectors, int *pnum) int64_t bytes, int64_t *pnum)
{ {
BlockDriverState *file; BlockDriverState *file;
int64_t ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum, 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); &file);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
if (pnum) {
*pnum = psectors * BDRV_SECTOR_SIZE;
}
return !!(ret & BDRV_BLOCK_ALLOCATED); return !!(ret & BDRV_BLOCK_ALLOCATED);
} }
/* /*
* Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP] * Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP]
* *
* Return true if the given sector is allocated in any image between * Return true if (a prefix of) the given range is allocated in any image
* BASE and TOP (inclusive). BASE can be NULL to check if the given * 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. * 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 * 'pnum' is set to the number of bytes (including and immediately
* the specified sector) that are known to be in the same * following the specified offset) that are known to be in the same
* allocated/unallocated state. * 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, int bdrv_is_allocated_above(BlockDriverState *top,
BlockDriverState *base, BlockDriverState *base,
int64_t sector_num, int64_t offset, int64_t bytes, int64_t *pnum)
int nb_sectors, int *pnum)
{ {
BlockDriverState *intermediate; BlockDriverState *intermediate;
int ret, n = nb_sectors; int ret;
int64_t n = bytes;
intermediate = top; intermediate = top;
while (intermediate && intermediate != base) { while (intermediate && intermediate != base) {
int pnum_inter; int64_t pnum_inter;
ret = bdrv_is_allocated(intermediate, sector_num, nb_sectors, int64_t size_inter;
&pnum_inter);
ret = bdrv_is_allocated(intermediate, offset, bytes, &pnum_inter);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} else if (ret) { }
if (ret) {
*pnum = pnum_inter; *pnum = pnum_inter;
return 1; return 1;
} }
/* size_inter = bdrv_getlength(intermediate);
* [sector_num, nb_sectors] is unallocated on top but intermediate if (size_inter < 0) {
* might have return size_inter;
* }
* [sector_num+x, nr_sectors] allocated.
*/
if (n > pnum_inter && if (n > pnum_inter &&
(intermediate == top || (intermediate == top || offset + pnum_inter < size_inter)) {
sector_num + pnum_inter < intermediate->total_sectors)) {
n = pnum_inter; n = pnum_inter;
} }

View File

@ -24,9 +24,8 @@
#define SLICE_TIME 100000000ULL /* ns */ #define SLICE_TIME 100000000ULL /* ns */
#define MAX_IN_FLIGHT 16 #define MAX_IN_FLIGHT 16
#define MAX_IO_SECTORS ((1 << 20) >> BDRV_SECTOR_BITS) /* 1 Mb */ #define MAX_IO_BYTES (1 << 20) /* 1 Mb */
#define DEFAULT_MIRROR_BUF_SIZE \ #define DEFAULT_MIRROR_BUF_SIZE (MAX_IN_FLIGHT * MAX_IO_BYTES)
(MAX_IN_FLIGHT * MAX_IO_SECTORS * BDRV_SECTOR_SIZE)
/* The mirroring buffer is a list of granularity-sized chunks. /* The mirroring buffer is a list of granularity-sized chunks.
* Free chunks are organized in a list. * Free chunks are organized in a list.
@ -67,11 +66,11 @@ typedef struct MirrorBlockJob {
uint64_t last_pause_ns; uint64_t last_pause_ns;
unsigned long *in_flight_bitmap; unsigned long *in_flight_bitmap;
int in_flight; int in_flight;
int64_t sectors_in_flight; int64_t bytes_in_flight;
int ret; int ret;
bool unmap; bool unmap;
bool waiting_for_io; bool waiting_for_io;
int target_cluster_sectors; int target_cluster_size;
int max_iov; int max_iov;
bool initial_zeroing_ongoing; bool initial_zeroing_ongoing;
} MirrorBlockJob; } MirrorBlockJob;
@ -79,8 +78,8 @@ typedef struct MirrorBlockJob {
typedef struct MirrorOp { typedef struct MirrorOp {
MirrorBlockJob *s; MirrorBlockJob *s;
QEMUIOVector qiov; QEMUIOVector qiov;
int64_t sector_num; int64_t offset;
int nb_sectors; uint64_t bytes;
} MirrorOp; } MirrorOp;
static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read, 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; MirrorBlockJob *s = op->s;
struct iovec *iov; struct iovec *iov;
int64_t chunk_num; 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->in_flight--;
s->sectors_in_flight -= op->nb_sectors; s->bytes_in_flight -= op->bytes;
iov = op->qiov.iov; iov = op->qiov.iov;
for (i = 0; i < op->qiov.niov; i++) { for (i = 0; i < op->qiov.niov; i++) {
MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base; MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base;
@ -114,16 +113,15 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
s->buf_free_count++; s->buf_free_count++;
} }
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; chunk_num = op->offset / s->granularity;
chunk_num = op->sector_num / sectors_per_chunk; nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity);
nb_chunks = DIV_ROUND_UP(op->nb_sectors, sectors_per_chunk);
bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks); bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks);
if (ret >= 0) { if (ret >= 0) {
if (s->cow_bitmap) { if (s->cow_bitmap) {
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks); bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
} }
if (!s->initial_zeroing_ongoing) { 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); qemu_iovec_destroy(&op->qiov);
@ -143,7 +141,8 @@ static void mirror_write_complete(void *opaque, int ret)
if (ret < 0) { if (ret < 0) {
BlockErrorAction action; 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); action = mirror_error_action(s, false, -ret);
if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) {
s->ret = ret; s->ret = ret;
@ -162,7 +161,8 @@ static void mirror_read_complete(void *opaque, int ret)
if (ret < 0) { if (ret < 0) {
BlockErrorAction action; 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); action = mirror_error_action(s, true, -ret);
if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) { if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) {
s->ret = ret; s->ret = ret;
@ -170,56 +170,53 @@ static void mirror_read_complete(void *opaque, int ret)
mirror_iteration_done(op, ret); mirror_iteration_done(op, ret);
} else { } 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); 0, mirror_write_complete, op);
} }
aio_context_release(blk_get_aio_context(s->common.blk)); aio_context_release(blk_get_aio_context(s->common.blk));
} }
static inline void mirror_clip_sectors(MirrorBlockJob *s, /* Clip bytes relative to offset to not exceed end-of-file */
int64_t sector_num, static inline int64_t mirror_clip_bytes(MirrorBlockJob *s,
int *nb_sectors) int64_t offset,
int64_t bytes)
{ {
*nb_sectors = MIN(*nb_sectors, return MIN(bytes, s->bdev_length - offset);
s->bdev_length / BDRV_SECTOR_SIZE - sector_num);
} }
/* Round sector_num and/or nb_sectors to target cluster if COW is needed, and /* Round offset and/or bytes to target cluster if COW is needed, and
* return the offset of the adjusted tail sector against original. */ * return the offset of the adjusted tail against original. */
static int mirror_cow_align(MirrorBlockJob *s, static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset,
int64_t *sector_num, uint64_t *bytes)
int *nb_sectors)
{ {
bool need_cow; bool need_cow;
int ret = 0; int ret = 0;
int chunk_sectors = s->granularity >> BDRV_SECTOR_BITS; int64_t align_offset = *offset;
int64_t align_sector_num = *sector_num; unsigned int align_bytes = *bytes;
int align_nb_sectors = *nb_sectors; int max_bytes = s->granularity * s->max_iov;
int max_sectors = chunk_sectors * s->max_iov;
need_cow = !test_bit(*sector_num / chunk_sectors, s->cow_bitmap); assert(*bytes < INT_MAX);
need_cow |= !test_bit((*sector_num + *nb_sectors - 1) / chunk_sectors, need_cow = !test_bit(*offset / s->granularity, s->cow_bitmap);
need_cow |= !test_bit((*offset + *bytes - 1) / s->granularity,
s->cow_bitmap); s->cow_bitmap);
if (need_cow) { if (need_cow) {
bdrv_round_sectors_to_clusters(blk_bs(s->target), *sector_num, bdrv_round_to_clusters(blk_bs(s->target), *offset, *bytes,
*nb_sectors, &align_sector_num, &align_offset, &align_bytes);
&align_nb_sectors);
} }
if (align_nb_sectors > max_sectors) { if (align_bytes > max_bytes) {
align_nb_sectors = max_sectors; align_bytes = max_bytes;
if (need_cow) { if (need_cow) {
align_nb_sectors = QEMU_ALIGN_DOWN(align_nb_sectors, align_bytes = QEMU_ALIGN_DOWN(align_bytes, s->target_cluster_size);
s->target_cluster_sectors);
} }
} }
/* 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. */ * 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); ret = align_offset + align_bytes - (*offset + *bytes);
*sector_num = align_sector_num; *offset = align_offset;
*nb_sectors = align_nb_sectors; *bytes = align_bytes;
assert(ret >= 0); assert(ret >= 0);
return ret; return ret;
} }
@ -233,50 +230,51 @@ static inline void mirror_wait_for_io(MirrorBlockJob *s)
} }
/* Submit async read while handling COW. /* Submit async read while handling COW.
* Returns: The number of sectors copied after and including sector_num, * Returns: The number of bytes copied after and including offset,
* excluding any sectors copied prior to sector_num due to alignment. * excluding any bytes copied prior to offset due to alignment.
* This will be nb_sectors if no alignment is necessary, or * This will be @bytes if no alignment is necessary, or
* (new_end - sector_num) if tail is rounded up or down due to * (new_end - offset) if tail is rounded up or down due to
* alignment or buffer limit. * alignment or buffer limit.
*/ */
static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num, static uint64_t mirror_do_read(MirrorBlockJob *s, int64_t offset,
int nb_sectors) uint64_t bytes)
{ {
BlockBackend *source = s->common.blk; BlockBackend *source = s->common.blk;
int sectors_per_chunk, nb_chunks; int nb_chunks;
int ret; uint64_t ret;
MirrorOp *op; MirrorOp *op;
int max_sectors; uint64_t max_bytes;
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; max_bytes = s->granularity * s->max_iov;
max_sectors = sectors_per_chunk * s->max_iov;
/* We can only handle as much as buf_size at a time. */ /* We can only handle as much as buf_size at a time. */
nb_sectors = MIN(s->buf_size >> BDRV_SECTOR_BITS, nb_sectors); bytes = MIN(s->buf_size, MIN(max_bytes, bytes));
nb_sectors = MIN(max_sectors, nb_sectors); assert(bytes);
assert(nb_sectors); assert(bytes < BDRV_REQUEST_MAX_BYTES);
ret = nb_sectors; ret = bytes;
if (s->cow_bitmap) { if (s->cow_bitmap) {
ret += mirror_cow_align(s, &sector_num, &nb_sectors); ret += mirror_cow_align(s, &offset, &bytes);
} }
assert(nb_sectors << BDRV_SECTOR_BITS <= s->buf_size); assert(bytes <= s->buf_size);
/* The sector range must meet granularity because: /* The offset is granularity-aligned because:
* 1) Caller passes in aligned values; * 1) Caller passes in aligned values;
* 2) mirror_cow_align is used only when target cluster is larger. */ * 2) mirror_cow_align is used only when target cluster is larger. */
assert(!(sector_num % sectors_per_chunk)); assert(QEMU_IS_ALIGNED(offset, s->granularity));
nb_chunks = DIV_ROUND_UP(nb_sectors, sectors_per_chunk); /* 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) { 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); mirror_wait_for_io(s);
} }
/* Allocate a MirrorOp that is used as an AIO callback. */ /* Allocate a MirrorOp that is used as an AIO callback. */
op = g_new(MirrorOp, 1); op = g_new(MirrorOp, 1);
op->s = s; op->s = s;
op->sector_num = sector_num; op->offset = offset;
op->nb_sectors = nb_sectors; op->bytes = bytes;
/* Now make a QEMUIOVector taking enough granularity-sized chunks /* Now make a QEMUIOVector taking enough granularity-sized chunks
* from s->buf_free. * 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); qemu_iovec_init(&op->qiov, nb_chunks);
while (nb_chunks-- > 0) { while (nb_chunks-- > 0) {
MirrorBuffer *buf = QSIMPLEQ_FIRST(&s->buf_free); 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); QSIMPLEQ_REMOVE_HEAD(&s->buf_free, next);
s->buf_free_count--; s->buf_free_count--;
@ -293,17 +291,16 @@ static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num,
/* Copy the dirty cluster. */ /* Copy the dirty cluster. */
s->in_flight++; s->in_flight++;
s->sectors_in_flight += nb_sectors; s->bytes_in_flight += bytes;
trace_mirror_one_iteration(s, sector_num, nb_sectors); trace_mirror_one_iteration(s, offset, bytes);
blk_aio_preadv(source, sector_num * BDRV_SECTOR_SIZE, &op->qiov, 0, blk_aio_preadv(source, offset, &op->qiov, 0, mirror_read_complete, op);
mirror_read_complete, op);
return ret; return ret;
} }
static void mirror_do_zero_or_discard(MirrorBlockJob *s, static void mirror_do_zero_or_discard(MirrorBlockJob *s,
int64_t sector_num, int64_t offset,
int nb_sectors, uint64_t bytes,
bool is_discard) bool is_discard)
{ {
MirrorOp *op; MirrorOp *op;
@ -312,19 +309,17 @@ static void mirror_do_zero_or_discard(MirrorBlockJob *s,
* so the freeing in mirror_iteration_done is nop. */ * so the freeing in mirror_iteration_done is nop. */
op = g_new0(MirrorOp, 1); op = g_new0(MirrorOp, 1);
op->s = s; op->s = s;
op->sector_num = sector_num; op->offset = offset;
op->nb_sectors = nb_sectors; op->bytes = bytes;
s->in_flight++; s->in_flight++;
s->sectors_in_flight += nb_sectors; s->bytes_in_flight += bytes;
if (is_discard) { if (is_discard) {
blk_aio_pdiscard(s->target, sector_num << BDRV_SECTOR_BITS, blk_aio_pdiscard(s->target, offset,
op->nb_sectors << BDRV_SECTOR_BITS, op->bytes, mirror_write_complete, op);
mirror_write_complete, op);
} else { } else {
blk_aio_pwrite_zeroes(s->target, sector_num * BDRV_SECTOR_SIZE, blk_aio_pwrite_zeroes(s->target, offset,
op->nb_sectors * BDRV_SECTOR_SIZE, op->bytes, s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
mirror_write_complete, op); 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) static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
{ {
BlockDriverState *source = s->source; BlockDriverState *source = s->source;
int64_t sector_num, first_chunk; int64_t offset, first_chunk;
uint64_t delay_ns = 0; uint64_t delay_ns = 0;
/* At least the first dirty chunk is mirrored in one iteration. */ /* At least the first dirty chunk is mirrored in one iteration. */
int nb_chunks = 1; int nb_chunks = 1;
int64_t end = s->bdev_length / BDRV_SECTOR_SIZE;
int sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS; int sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target)); 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, int max_io_bytes = MAX(s->buf_size / MAX_IN_FLIGHT, MAX_IO_BYTES);
MAX_IO_SECTORS);
bdrv_dirty_bitmap_lock(s->dirty_bitmap); bdrv_dirty_bitmap_lock(s->dirty_bitmap);
sector_num = bdrv_dirty_iter_next(s->dbi); offset = bdrv_dirty_iter_next(s->dbi) * BDRV_SECTOR_SIZE;
if (sector_num < 0) { if (offset < 0) {
bdrv_set_dirty_iter(s->dbi, 0); bdrv_set_dirty_iter(s->dbi, 0);
sector_num = bdrv_dirty_iter_next(s->dbi); offset = bdrv_dirty_iter_next(s->dbi) * BDRV_SECTOR_SIZE;
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap)); trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap) *
assert(sector_num >= 0); BDRV_SECTOR_SIZE);
assert(offset >= 0);
} }
bdrv_dirty_bitmap_unlock(s->dirty_bitmap); 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)) { 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); 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 /* Find the number of consective dirty chunks following the first dirty
* one, and wait for in flight requests in them. */ * one, and wait for in flight requests in them. */
bdrv_dirty_bitmap_lock(s->dirty_bitmap); 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_dirty;
int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk; int64_t next_offset = offset + nb_chunks * s->granularity;
int64_t next_chunk = next_sector / sectors_per_chunk; int64_t next_chunk = next_offset / s->granularity;
if (next_sector >= end || if (next_offset >= s->bdev_length ||
!bdrv_get_dirty_locked(source, s->dirty_bitmap, next_sector)) { !bdrv_get_dirty_locked(source, s->dirty_bitmap,
next_offset >> BDRV_SECTOR_BITS)) {
break; break;
} }
if (test_bit(next_chunk, s->in_flight_bitmap)) { if (test_bit(next_chunk, s->in_flight_bitmap)) {
break; break;
} }
next_dirty = bdrv_dirty_iter_next(s->dbi); next_dirty = bdrv_dirty_iter_next(s->dbi) * BDRV_SECTOR_SIZE;
if (next_dirty > next_sector || next_dirty < 0) { if (next_dirty > next_offset || next_dirty < 0) {
/* The bitmap iterator's cache is stale, refresh it */ /* The bitmap iterator's cache is stale, refresh it */
bdrv_set_dirty_iter(s->dbi, next_sector); bdrv_set_dirty_iter(s->dbi, next_offset >> BDRV_SECTOR_BITS);
next_dirty = bdrv_dirty_iter_next(s->dbi); next_dirty = bdrv_dirty_iter_next(s->dbi) * BDRV_SECTOR_SIZE;
} }
assert(next_dirty == next_sector); assert(next_dirty == next_offset);
nb_chunks++; 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 * calling bdrv_get_block_status_above could yield - if some blocks are
* marked dirty in this window, we need to know. * marked dirty in this window, we need to know.
*/ */
bdrv_reset_dirty_bitmap_locked(s->dirty_bitmap, sector_num, bdrv_reset_dirty_bitmap_locked(s->dirty_bitmap, offset >> BDRV_SECTOR_BITS,
nb_chunks * sectors_per_chunk); nb_chunks * sectors_per_chunk);
bdrv_dirty_bitmap_unlock(s->dirty_bitmap); bdrv_dirty_bitmap_unlock(s->dirty_bitmap);
bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks); bitmap_set(s->in_flight_bitmap, offset / s->granularity, nb_chunks);
while (nb_chunks > 0 && sector_num < end) { while (nb_chunks > 0 && offset < s->bdev_length) {
int64_t ret; int64_t ret;
int io_sectors, io_sectors_acct; int io_sectors;
unsigned int io_bytes;
int64_t io_bytes_acct;
BlockDriverState *file; BlockDriverState *file;
enum MirrorMethod { enum MirrorMethod {
MIRROR_METHOD_COPY, MIRROR_METHOD_COPY,
@ -404,27 +401,28 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
MIRROR_METHOD_DISCARD MIRROR_METHOD_DISCARD
} mirror_method = MIRROR_METHOD_COPY; } mirror_method = MIRROR_METHOD_COPY;
assert(!(sector_num % sectors_per_chunk)); assert(!(offset % s->granularity));
ret = bdrv_get_block_status_above(source, NULL, sector_num, ret = bdrv_get_block_status_above(source, NULL,
offset >> BDRV_SECTOR_BITS,
nb_chunks * sectors_per_chunk, nb_chunks * sectors_per_chunk,
&io_sectors, &file); &io_sectors, &file);
io_bytes = io_sectors * BDRV_SECTOR_SIZE;
if (ret < 0) { 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) { } 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; io_bytes -= io_bytes % s->granularity;
if (io_sectors < sectors_per_chunk) { if (io_bytes < s->granularity) {
io_sectors = sectors_per_chunk; io_bytes = s->granularity;
} else if (ret >= 0 && !(ret & BDRV_BLOCK_DATA)) { } else if (ret >= 0 && !(ret & BDRV_BLOCK_DATA)) {
int64_t target_sector_num; int64_t target_offset;
int target_nb_sectors; unsigned int target_bytes;
bdrv_round_sectors_to_clusters(blk_bs(s->target), sector_num, bdrv_round_to_clusters(blk_bs(s->target), offset, io_bytes,
io_sectors, &target_sector_num, &target_offset, &target_bytes);
&target_nb_sectors); if (target_offset == offset &&
if (target_sector_num == sector_num && target_bytes == io_bytes) {
target_nb_sectors == io_sectors) {
mirror_method = ret & BDRV_BLOCK_ZERO ? mirror_method = ret & BDRV_BLOCK_ZERO ?
MIRROR_METHOD_ZERO : MIRROR_METHOD_ZERO :
MIRROR_METHOD_DISCARD; MIRROR_METHOD_DISCARD;
@ -432,7 +430,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
} }
while (s->in_flight >= MAX_IN_FLIGHT) { 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); mirror_wait_for_io(s);
} }
@ -440,30 +438,29 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
return 0; return 0;
} }
mirror_clip_sectors(s, sector_num, &io_sectors); io_bytes = mirror_clip_bytes(s, offset, io_bytes);
switch (mirror_method) { switch (mirror_method) {
case MIRROR_METHOD_COPY: case MIRROR_METHOD_COPY:
io_sectors = mirror_do_read(s, sector_num, io_sectors); io_bytes = io_bytes_acct = mirror_do_read(s, offset, io_bytes);
io_sectors_acct = io_sectors;
break; break;
case MIRROR_METHOD_ZERO: case MIRROR_METHOD_ZERO:
case MIRROR_METHOD_DISCARD: 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); mirror_method == MIRROR_METHOD_DISCARD);
if (write_zeroes_ok) { if (write_zeroes_ok) {
io_sectors_acct = 0; io_bytes_acct = 0;
} else { } else {
io_sectors_acct = io_sectors; io_bytes_acct = io_bytes;
} }
break; break;
default: default:
abort(); abort();
} }
assert(io_sectors); assert(io_bytes);
sector_num += io_sectors; offset += io_bytes;
nb_chunks -= DIV_ROUND_UP(io_sectors, sectors_per_chunk); nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity);
if (s->common.speed) { 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; return delay_ns;
@ -624,6 +621,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
BlockDriverState *bs = s->source; BlockDriverState *bs = s->source;
BlockDriverState *target_bs = blk_bs(s->target); BlockDriverState *target_bs = blk_bs(s->target);
int ret, n; int ret, n;
int64_t count;
end = s->bdev_length / BDRV_SECTOR_SIZE; end = s->bdev_length / BDRV_SECTOR_SIZE;
@ -652,7 +650,8 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
continue; 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; sector_num += nb_sectors;
} }
@ -672,11 +671,16 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
return 0; 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) { if (ret < 0) {
return ret; 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); assert(n > 0);
if (ret == 1) { if (ret == 1) {
bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n); 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 char backing_filename[2]; /* we only need 2 characters because we are only
checking for a NULL string */ checking for a NULL string */
int ret = 0; int ret = 0;
int target_cluster_size = BDRV_SECTOR_SIZE;
if (block_job_is_cancelled(&s->common)) { if (block_job_is_cancelled(&s->common)) {
goto immediate_exit; goto immediate_exit;
@ -764,14 +767,15 @@ static void coroutine_fn mirror_run(void *opaque)
bdrv_get_backing_filename(target_bs, backing_filename, bdrv_get_backing_filename(target_bs, backing_filename,
sizeof(backing_filename)); sizeof(backing_filename));
if (!bdrv_get_info(target_bs, &bdi) && bdi.cluster_size) { 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 if (backing_filename[0] && !target_bs->backing &&
&& s->granularity < target_cluster_size) { s->granularity < s->target_cluster_size) {
s->buf_size = MAX(s->buf_size, target_cluster_size); s->buf_size = MAX(s->buf_size, s->target_cluster_size);
s->cow_bitmap = bitmap_new(length); 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->max_iov = MIN(bs->bl.max_iov, target_bs->bl.max_iov);
s->buf = qemu_try_blockalign(bs, s->buf_size); 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); cnt = bdrv_get_dirty_count(s->dirty_bitmap);
/* s->common.offset contains the number of bytes already processed so /* s->common.offset contains the number of bytes already processed so
* far, cnt is the number of dirty sectors remaining and * 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 */ * processed; together those are the current total operation length */
s->common.len = s->common.offset + s->common.len = s->common.offset + s->bytes_in_flight +
(cnt + s->sectors_in_flight) * BDRV_SECTOR_SIZE; cnt * BDRV_SECTOR_SIZE;
/* Note that even when no rate limit is applied we need to yield /* 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. * 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) { s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 || if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 ||
(cnt == 0 && s->in_flight > 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); mirror_wait_for_io(s);
continue; continue;
} else if (cnt != 0) { } 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 * 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. * 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); bdrv_drained_begin(bs);
cnt = bdrv_get_dirty_count(s->dirty_bitmap); cnt = bdrv_get_dirty_count(s->dirty_bitmap);
@ -882,7 +887,8 @@ static void coroutine_fn mirror_run(void *opaque)
} }
ret = 0; 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) { if (!s->synced) {
block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns); block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns);
if (block_job_is_cancelled(&s->common)) { 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"); error_setg(errp, QERR_INVALID_PARAMETER, "speed");
return; 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) 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; *pnum = nb_sectors;
*file = bs->backing->bs; *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); (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); assert ((granularity & (granularity - 1)) == 0);
/* Granularity must be large enough for sector-based dirty bitmap */
assert(granularity >= BDRV_SECTOR_SIZE);
if (buf_size < 0) { if (buf_size < 0) {
error_setg(errp, "Invalid parameter 'buf-size'"); error_setg(errp, "Invalid parameter 'buf-size'");

View File

@ -259,7 +259,7 @@ static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
*pnum = nb_sectors; *pnum = nb_sectors;
*file = bs->file->bs; *file = bs->file->bs;
sector_num += s->offset / BDRV_SECTOR_SIZE; 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); (sector_num << BDRV_SECTOR_BITS);
} }

View File

@ -234,10 +234,14 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs,
} }
if (job) { if (job) {
backup_wait_for_overlapping_requests(child->bs->job, sector_num, uint64_t remaining_bytes = remaining_sectors * BDRV_SECTOR_SIZE;
remaining_sectors);
backup_cow_request_begin(&req, child->bs->job, sector_num, backup_wait_for_overlapping_requests(child->bs->job,
remaining_sectors); 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, ret = bdrv_co_readv(bs->file, sector_num, remaining_sectors,
qiov); qiov);
backup_cow_request_end(&req); backup_cow_request_end(&req);
@ -260,7 +264,8 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
BdrvChild *top = bs->file; BdrvChild *top = bs->file;
BdrvChild *base = s->secondary_disk; BdrvChild *base = s->secondary_disk;
BdrvChild *target; BdrvChild *target;
int ret, n; int ret;
int64_t n;
ret = replication_get_io_status(s); ret = replication_get_io_status(s);
if (ret < 0) { if (ret < 0) {
@ -279,14 +284,20 @@ static coroutine_fn int replication_co_writev(BlockDriverState *bs,
*/ */
qemu_iovec_init(&hd_qiov, qiov->niov); qemu_iovec_init(&hd_qiov, qiov->niov);
while (remaining_sectors > 0) { while (remaining_sectors > 0) {
ret = bdrv_is_allocated_above(top->bs, base->bs, sector_num, int64_t count;
remaining_sectors, &n);
ret = bdrv_is_allocated_above(top->bs, base->bs,
sector_num * BDRV_SECTOR_SIZE,
remaining_sectors * BDRV_SECTOR_SIZE,
&count);
if (ret < 0) { if (ret < 0) {
goto out1; goto out1;
} }
assert(QEMU_IS_ALIGNED(count, BDRV_SECTOR_SIZE));
n = count >> BDRV_SECTOR_BITS;
qemu_iovec_reset(&hd_qiov); 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; target = ret ? top : base;
ret = bdrv_co_writev(target, sector_num, n, &hd_qiov); 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; remaining_sectors -= n;
sector_num += n; sector_num += n;
bytes_done += n * BDRV_SECTOR_SIZE; bytes_done += count;
} }
out1: out1:

View File

@ -41,25 +41,24 @@ typedef struct StreamBlockJob {
} StreamBlockJob; } StreamBlockJob;
static int coroutine_fn stream_populate(BlockBackend *blk, static int coroutine_fn stream_populate(BlockBackend *blk,
int64_t sector_num, int nb_sectors, int64_t offset, uint64_t bytes,
void *buf) void *buf)
{ {
struct iovec iov = { struct iovec iov = {
.iov_base = buf, .iov_base = buf,
.iov_len = nb_sectors * BDRV_SECTOR_SIZE, .iov_len = bytes,
}; };
QEMUIOVector qiov; QEMUIOVector qiov;
assert(bytes < SIZE_MAX);
qemu_iovec_init_external(&qiov, &iov, 1); qemu_iovec_init_external(&qiov, &iov, 1);
/* Copy-on-read the unallocated clusters */ /* Copy-on-read the unallocated clusters */
return blk_co_preadv(blk, sector_num * BDRV_SECTOR_SIZE, qiov.size, &qiov, return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
BDRV_REQ_COPY_ON_READ);
} }
typedef struct { typedef struct {
int ret; int ret;
bool reached_end;
} StreamCompleteData; } StreamCompleteData;
static void stream_complete(BlockJob *job, void *opaque) static void stream_complete(BlockJob *job, void *opaque)
@ -70,7 +69,7 @@ static void stream_complete(BlockJob *job, void *opaque)
BlockDriverState *base = s->base; BlockDriverState *base = s->base;
Error *local_err = NULL; 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) { data->ret == 0) {
const char *base_id = NULL, *base_fmt = NULL; const char *base_id = NULL, *base_fmt = NULL;
if (base) { if (base) {
@ -108,12 +107,11 @@ static void coroutine_fn stream_run(void *opaque)
BlockBackend *blk = s->common.blk; BlockBackend *blk = s->common.blk;
BlockDriverState *bs = blk_bs(blk); BlockDriverState *bs = blk_bs(blk);
BlockDriverState *base = s->base; BlockDriverState *base = s->base;
int64_t sector_num = 0; int64_t offset = 0;
int64_t end = -1;
uint64_t delay_ns = 0; uint64_t delay_ns = 0;
int error = 0; int error = 0;
int ret = 0; int ret = 0;
int n = 0; int64_t n = 0; /* bytes */
void *buf; void *buf;
if (!bs->backing) { if (!bs->backing) {
@ -126,7 +124,6 @@ static void coroutine_fn stream_run(void *opaque)
goto out; goto out;
} }
end = s->common.len >> BDRV_SECTOR_BITS;
buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE); buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE);
/* Turn on copy-on-read for the whole block device so that guest read /* 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); 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; bool copy;
/* Note that even when no rate limit is applied we need to yield /* 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; copy = false;
ret = bdrv_is_allocated(bs, sector_num, ret = bdrv_is_allocated(bs, offset, STREAM_BUFFER_SIZE, &n);
STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n);
if (ret == 1) { if (ret == 1) {
/* Allocated in the top, no need to copy. */ /* Allocated in the top, no need to copy. */
} else if (ret >= 0) { } else if (ret >= 0) {
/* Copy if allocated in the intermediate images. Limit to the /* 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, 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 */ /* Finish early if end of backing file has been reached */
if (ret == 0 && n == 0) { if (ret == 0 && n == 0) {
n = end - sector_num; n = s->common.len - offset;
} }
copy = (ret == 1); copy = (ret == 1);
} }
trace_stream_one_iteration(s, sector_num, n, ret); trace_stream_one_iteration(s, offset, n, ret);
if (copy) { if (copy) {
ret = stream_populate(blk, sector_num, n, buf); ret = stream_populate(blk, offset, n, buf);
} }
if (ret < 0) { if (ret < 0) {
BlockErrorAction action = BlockErrorAction action =
@ -189,7 +185,7 @@ static void coroutine_fn stream_run(void *opaque)
ret = 0; ret = 0;
/* Publish progress */ /* Publish progress */
s->common.offset += n * BDRV_SECTOR_SIZE; s->common.offset += n;
if (copy && s->common.speed) { if (copy && s->common.speed) {
delay_ns = ratelimit_calculate_delay(&s->limit, n); delay_ns = ratelimit_calculate_delay(&s->limit, n);
} }
@ -208,7 +204,6 @@ out:
/* Modify backing chain and close BDSes in main loop */ /* Modify backing chain and close BDSes in main loop */
data = g_malloc(sizeof(*data)); data = g_malloc(sizeof(*data));
data->ret = ret; data->ret = ret;
data->reached_end = sector_num == end;
block_job_defer_to_main_loop(&s->common, stream_complete, data); 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"); error_setg(errp, QERR_INVALID_PARAMETER, "speed");
return; 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 = { static const BlockJobDriver stream_job_driver = {

View File

@ -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" 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 # 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" stream_start(void *bs, void *base, void *s) "bs %p base %p s %p"
# block/commit.c # 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" commit_start(void *bs, void *base, void *top, void *s) "bs %p base %p top %p s %p"
# block/mirror.c # 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_flush(void *s) "s %p"
mirror_before_drain(void *s, int64_t cnt) "s %p dirty count %"PRId64 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_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_one_iteration(void *s, int64_t offset, uint64_t bytes) "s %p offset %" PRId64 " bytes %" PRIu64
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_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(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 # 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_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 sector_num, int nb_sectors, int ret) "job %p sector_num %"PRId64" nb_sectors %d ret %d" 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_skip(void *job, int64_t start) "job %p start %"PRId64
backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64 backup_do_cow_process(void *job, int64_t start) "job %p start %"PRId64
backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d"

View File

@ -701,7 +701,7 @@ static int64_t coroutine_fn vpc_co_get_block_status(BlockDriverState *bs,
if (be32_to_cpu(footer->type) == VHD_FIXED) { if (be32_to_cpu(footer->type) == VHD_FIXED) {
*pnum = nb_sectors; *pnum = nb_sectors;
*file = bs->file->bs; *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); (sector_num << BDRV_SECTOR_BITS);
} }

View File

@ -217,23 +217,30 @@ typedef struct bootsector_t {
union { union {
struct { struct {
uint8_t drive_number; uint8_t drive_number;
uint8_t current_head; uint8_t reserved1;
uint8_t signature; uint8_t signature;
uint32_t id; uint32_t id;
uint8_t volume_label[11]; uint8_t volume_label[11];
uint8_t fat_type[8];
uint8_t ignored[0x1c0];
} QEMU_PACKED fat16; } QEMU_PACKED fat16;
struct { struct {
uint32_t sectors_per_fat; uint32_t sectors_per_fat;
uint16_t flags; uint16_t flags;
uint8_t major,minor; uint8_t major,minor;
uint32_t first_cluster_of_root_directory; uint32_t first_cluster_of_root_dir;
uint16_t info_sector; uint16_t info_sector;
uint16_t backup_boot_sector; uint16_t backup_boot_sector;
uint16_t ignored; uint8_t reserved[12];
uint8_t drive_number;
uint8_t reserved1;
uint8_t signature;
uint32_t id;
uint8_t volume_label[11];
uint8_t fat_type[8];
uint8_t ignored[0x1a4];
} QEMU_PACKED fat32; } QEMU_PACKED fat32;
} u; } u;
uint8_t fat_type[8];
uint8_t ignored[0x1c0];
uint8_t magic[2]; uint8_t magic[2];
} QEMU_PACKED bootsector_t; } QEMU_PACKED bootsector_t;
@ -286,8 +293,7 @@ typedef struct mapping_t {
union { union {
/* offset is /* offset is
* - the offset in the file (in clusters) for a file, or * - the offset in the file (in clusters) for a file, or
* - the next cluster of the directory for a directory, and * - the next cluster of the directory for a directory
* - the address of the buffer for a faked entry
*/ */
struct { struct {
uint32_t offset; uint32_t offset;
@ -300,9 +306,13 @@ typedef struct mapping_t {
/* path contains the full path, i.e. it always starts with s->path */ /* path contains the full path, i.e. it always starts with s->path */
char* path; char* path;
enum { MODE_UNDEFINED = 0, MODE_NORMAL = 1, MODE_MODIFIED = 2, enum {
MODE_DIRECTORY = 4, MODE_FAKED = 8, MODE_UNDEFINED = 0,
MODE_DELETED = 16, MODE_RENAMED = 32 } mode; MODE_NORMAL = 1,
MODE_MODIFIED = 2,
MODE_DIRECTORY = 4,
MODE_DELETED = 8,
} mode;
int read_only; int read_only;
} mapping_t; } mapping_t;
@ -316,22 +326,25 @@ static void print_mapping(const struct mapping_t* mapping);
typedef struct BDRVVVFATState { typedef struct BDRVVVFATState {
CoMutex lock; CoMutex lock;
BlockDriverState* bs; /* pointer to parent */ BlockDriverState* bs; /* pointer to parent */
unsigned int first_sectors_number; /* 1 for a single partition, 0x40 for a disk with partition table */
unsigned char first_sectors[0x40*0x200]; unsigned char first_sectors[0x40*0x200];
int fat_type; /* 16 or 32 */ int fat_type; /* 16 or 32 */
array_t fat,directory,mapping; array_t fat,directory,mapping;
char volume_label[11]; char volume_label[11];
uint32_t offset_to_bootsector; /* 0 for floppy, 0x3f for disk */
unsigned int cluster_size; unsigned int cluster_size;
unsigned int sectors_per_cluster; unsigned int sectors_per_cluster;
unsigned int sectors_per_fat; unsigned int sectors_per_fat;
unsigned int sectors_of_root_directory;
uint32_t last_cluster_of_root_directory; uint32_t last_cluster_of_root_directory;
unsigned int faked_sectors; /* how many sectors are faked before file data */ /* how many entries are available in root directory (0 for FAT32) */
uint16_t root_entries;
uint32_t sector_count; /* total number of sectors of the partition */ uint32_t sector_count; /* total number of sectors of the partition */
uint32_t cluster_count; /* total number of clusters of this partition */ uint32_t cluster_count; /* total number of clusters of this partition */
uint32_t max_fat_value; uint32_t max_fat_value;
uint32_t offset_to_fat;
uint32_t offset_to_root_dir;
int current_fd; int current_fd;
mapping_t* current_mapping; mapping_t* current_mapping;
@ -390,50 +403,41 @@ static void init_mbr(BDRVVVFATState *s, int cyls, int heads, int secs)
partition->attributes=0x80; /* bootable */ partition->attributes=0x80; /* bootable */
/* LBA is used when partition is outside the CHS geometry */ /* LBA is used when partition is outside the CHS geometry */
lba = sector2CHS(&partition->start_CHS, s->first_sectors_number - 1, lba = sector2CHS(&partition->start_CHS, s->offset_to_bootsector,
cyls, heads, secs); cyls, heads, secs);
lba |= sector2CHS(&partition->end_CHS, s->bs->total_sectors - 1, lba |= sector2CHS(&partition->end_CHS, s->bs->total_sectors - 1,
cyls, heads, secs); cyls, heads, secs);
/*LBA partitions are identified only by start/length_sector_long not by CHS*/ /*LBA partitions are identified only by start/length_sector_long not by CHS*/
partition->start_sector_long = cpu_to_le32(s->first_sectors_number - 1); partition->start_sector_long = cpu_to_le32(s->offset_to_bootsector);
partition->length_sector_long = cpu_to_le32(s->bs->total_sectors partition->length_sector_long = cpu_to_le32(s->bs->total_sectors
- s->first_sectors_number + 1); - s->offset_to_bootsector);
/* FAT12/FAT16/FAT32 */ /* FAT12/FAT16/FAT32 */
/* DOS uses different types when partition is LBA, /* DOS uses different types when partition is LBA,
probably to prevent older versions from using CHS on them */ probably to prevent older versions from using CHS on them */
partition->fs_type= s->fat_type==12 ? 0x1: partition->fs_type = s->fat_type == 12 ? 0x1 :
s->fat_type==16 ? (lba?0xe:0x06): s->fat_type == 16 ? (lba ? 0xe : 0x06) :
/*fat_tyoe==32*/ (lba?0xc:0x0b); /*s->fat_type == 32*/ (lba ? 0xc : 0x0b);
real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa; real_mbr->magic[0]=0x55; real_mbr->magic[1]=0xaa;
} }
/* direntry functions */ /* direntry functions */
/* dest is assumed to hold 258 bytes, and pads with 0xffff up to next multiple of 26 */ static direntry_t *create_long_filename(BDRVVVFATState *s, const char *filename)
static inline int short2long_name(char* dest,const char* src)
{ {
int i; int number_of_entries, i;
int len; glong length;
for(i=0;i<129 && src[i];i++) { direntry_t *entry;
dest[2*i]=src[i];
dest[2*i+1]=0;
}
len=2*i;
dest[2*i]=dest[2*i+1]=0;
for(i=2*i+2;(i%26);i++)
dest[i]=0xff;
return len;
}
static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* filename) gunichar2 *longname = g_utf8_to_utf16(filename, -1, NULL, &length, NULL);
{ if (!longname) {
char buffer[258]; fprintf(stderr, "vvfat: invalid UTF-8 name: %s\n", filename);
int length=short2long_name(buffer,filename), return NULL;
number_of_entries=(length+25)/26,i; }
direntry_t* entry;
number_of_entries = (length * 2 + 25) / 26;
for(i=0;i<number_of_entries;i++) { for(i=0;i<number_of_entries;i++) {
entry=array_get_next(&(s->directory)); entry=array_get_next(&(s->directory));
@ -448,8 +452,15 @@ static inline direntry_t* create_long_filename(BDRVVVFATState* s,const char* fil
else if(offset<22) offset=14+offset-10; else if(offset<22) offset=14+offset-10;
else offset=28+offset-22; else offset=28+offset-22;
entry=array_get(&(s->directory),s->directory.next-1-(i/26)); entry=array_get(&(s->directory),s->directory.next-1-(i/26));
entry->name[offset]=buffer[i]; if (i >= 2 * length + 2) {
entry->name[offset] = 0xff;
} else if (i % 2 == 0) {
entry->name[offset] = longname[i / 2] & 0xff;
} else {
entry->name[offset] = longname[i / 2] >> 8;
} }
}
g_free(longname);
return array_get(&(s->directory),s->directory.next-number_of_entries); return array_get(&(s->directory),s->directory.next-number_of_entries);
} }
@ -505,6 +516,110 @@ static void set_begin_of_direntry(direntry_t* direntry, uint32_t begin)
direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff); direntry->begin_hi = cpu_to_le16((begin >> 16) & 0xffff);
} }
static uint8_t to_valid_short_char(gunichar c)
{
c = g_unichar_toupper(c);
if ((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
strchr("$%'-_@~`!(){}^#&", c) != 0) {
return c;
} else {
return 0;
}
}
static direntry_t *create_short_filename(BDRVVVFATState *s,
const char *filename,
unsigned int directory_start)
{
int i, j = 0;
direntry_t *entry = array_get_next(&(s->directory));
const gchar *p, *last_dot = NULL;
gunichar c;
bool lossy_conversion = false;
char tail[11];
if (!entry) {
return NULL;
}
memset(entry->name, 0x20, sizeof(entry->name));
/* copy filename and search last dot */
for (p = filename; ; p = g_utf8_next_char(p)) {
c = g_utf8_get_char(p);
if (c == '\0') {
break;
} else if (c == '.') {
if (j == 0) {
/* '.' at start of filename */
lossy_conversion = true;
} else {
if (last_dot) {
lossy_conversion = true;
}
last_dot = p;
}
} else if (!last_dot) {
/* first part of the name; copy it */
uint8_t v = to_valid_short_char(c);
if (j < 8 && v) {
entry->name[j++] = v;
} else {
lossy_conversion = true;
}
}
}
/* copy extension (if any) */
if (last_dot) {
j = 0;
for (p = g_utf8_next_char(last_dot); ; p = g_utf8_next_char(p)) {
c = g_utf8_get_char(p);
if (c == '\0') {
break;
} else {
/* extension; copy it */
uint8_t v = to_valid_short_char(c);
if (j < 3 && v) {
entry->name[8 + (j++)] = v;
} else {
lossy_conversion = true;
}
}
}
}
if (entry->name[0] == 0xe5) {
entry->name[0] = 0x05;
}
/* numeric-tail generation */
for (j = 0; j < 8; j++) {
if (entry->name[j] == ' ') {
break;
}
}
for (i = lossy_conversion ? 1 : 0; i < 999999; i++) {
direntry_t *entry1;
if (i > 0) {
int len = sprintf(tail, "~%d", i);
memcpy(entry->name + MIN(j, 8 - len), tail, len);
}
for (entry1 = array_get(&(s->directory), directory_start);
entry1 < entry; entry1++) {
if (!is_long_name(entry1) &&
!memcmp(entry1->name, entry->name, 11)) {
break; /* found dupe */
}
}
if (entry1 == entry) {
/* no dupe found */
return entry;
}
}
return NULL;
}
/* fat functions */ /* fat functions */
static inline uint8_t fat_chksum(const direntry_t* entry) static inline uint8_t fat_chksum(const direntry_t* entry)
@ -598,12 +713,10 @@ static inline void init_fat(BDRVVVFATState* s)
} }
/* TODO: in create_short_filename, 0xe5->0x05 is not yet handled! */
/* TODO: in parse_short_filename, 0x05->0xe5 is not yet handled! */
static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s, static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
unsigned int directory_start, const char* filename, int is_dot) unsigned int directory_start, const char* filename, int is_dot)
{ {
int i,j,long_index=s->directory.next; int long_index = s->directory.next;
direntry_t* entry = NULL; direntry_t* entry = NULL;
direntry_t* entry_long = NULL; direntry_t* entry_long = NULL;
@ -615,62 +728,7 @@ static inline direntry_t* create_short_and_long_name(BDRVVVFATState* s,
} }
entry_long=create_long_filename(s,filename); entry_long=create_long_filename(s,filename);
entry = create_short_filename(s, filename, directory_start);
i = strlen(filename);
for(j = i - 1; j>0 && filename[j]!='.';j--);
if (j > 0)
i = (j > 8 ? 8 : j);
else if (i > 8)
i = 8;
entry=array_get_next(&(s->directory));
memset(entry->name, 0x20, sizeof(entry->name));
memcpy(entry->name, filename, i);
if (j > 0) {
for (i = 0; i < 3 && filename[j + 1 + i]; i++) {
entry->name[8 + i] = filename[j + 1 + i];
}
}
/* upcase & remove unwanted characters */
for(i=10;i>=0;i--) {
if(i==10 || i==7) for(;i>0 && entry->name[i]==' ';i--);
if(entry->name[i]<=' ' || entry->name[i]>0x7f
|| strchr(".*?<>|\":/\\[];,+='",entry->name[i]))
entry->name[i]='_';
else if(entry->name[i]>='a' && entry->name[i]<='z')
entry->name[i]+='A'-'a';
}
/* mangle duplicates */
while(1) {
direntry_t* entry1=array_get(&(s->directory),directory_start);
int j;
for(;entry1<entry;entry1++)
if(!is_long_name(entry1) && !memcmp(entry1->name,entry->name,11))
break; /* found dupe */
if(entry1==entry) /* no dupe found */
break;
/* use all 8 characters of name */
if(entry->name[7]==' ') {
int j;
for(j=6;j>0 && entry->name[j]==' ';j--)
entry->name[j]='~';
}
/* increment number */
for(j=7;j>0 && entry->name[j]=='9';j--)
entry->name[j]='0';
if(j>0) {
if(entry->name[j]<'0' || entry->name[j]>'9')
entry->name[j]='0';
else
entry->name[j]++;
}
}
/* calculate checksum; propagate to long name */ /* calculate checksum; propagate to long name */
if(entry_long) { if(entry_long) {
@ -715,6 +773,12 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
i = mapping->info.dir.first_dir_index = i = mapping->info.dir.first_dir_index =
first_cluster == 0 ? 0 : s->directory.next; first_cluster == 0 ? 0 : s->directory.next;
if (first_cluster != 0) {
/* create the top entries of a subdirectory */
(void)create_short_and_long_name(s, i, ".", 1);
(void)create_short_and_long_name(s, i, "..", 1);
}
/* actually read the directory, and allocate the mappings */ /* actually read the directory, and allocate the mappings */
while((entry=readdir(dir))) { while((entry=readdir(dir))) {
unsigned int length=strlen(dirname)+2+strlen(entry->d_name); unsigned int length=strlen(dirname)+2+strlen(entry->d_name);
@ -724,6 +788,12 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
int is_dot=!strcmp(entry->d_name,"."); int is_dot=!strcmp(entry->d_name,".");
int is_dotdot=!strcmp(entry->d_name,".."); int is_dotdot=!strcmp(entry->d_name,"..");
if (first_cluster == 0 && s->directory.next >= s->root_entries - 1) {
fprintf(stderr, "Too many entries in root directory\n");
closedir(dir);
return -2;
}
if(first_cluster == 0 && (is_dotdot || is_dot)) if(first_cluster == 0 && (is_dotdot || is_dot))
continue; continue;
@ -736,8 +806,11 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
} }
/* create directory entry for this file */ /* create directory entry for this file */
direntry=create_short_and_long_name(s, i, entry->d_name, if (!is_dot && !is_dotdot) {
is_dot || is_dotdot); direntry = create_short_and_long_name(s, i, entry->d_name, 0);
} else {
direntry = array_get(&(s->directory), is_dot ? i : i + 1);
}
direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20); direntry->attributes=(S_ISDIR(st.st_mode)?0x10:0x20);
direntry->reserved[0]=direntry->reserved[1]=0; direntry->reserved[0]=direntry->reserved[1]=0;
direntry->ctime=fat_datetime(st.st_ctime,1); direntry->ctime=fat_datetime(st.st_ctime,1);
@ -794,18 +867,18 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
memset(direntry,0,sizeof(direntry_t)); memset(direntry,0,sizeof(direntry_t));
} }
/* TODO: if there are more entries, bootsector has to be adjusted! */ if (s->fat_type != 32 &&
#define ROOT_ENTRIES (0x02 * 0x10 * s->sectors_per_cluster) mapping_index == 0 &&
if (mapping_index == 0 && s->directory.next < ROOT_ENTRIES) { s->directory.next < s->root_entries) {
/* root directory */ /* root directory */
int cur = s->directory.next; int cur = s->directory.next;
array_ensure_allocated(&(s->directory), ROOT_ENTRIES - 1); array_ensure_allocated(&(s->directory), s->root_entries - 1);
s->directory.next = ROOT_ENTRIES; s->directory.next = s->root_entries;
memset(array_get(&(s->directory), cur), 0, memset(array_get(&(s->directory), cur), 0,
(ROOT_ENTRIES - cur) * sizeof(direntry_t)); (s->root_entries - cur) * sizeof(direntry_t));
} }
/* reget the mapping, since s->mapping was possibly realloc()ed */ /* re-get the mapping, since s->mapping was possibly realloc()ed */
mapping = array_get(&(s->mapping), mapping_index); mapping = array_get(&(s->mapping), mapping_index);
first_cluster += (s->directory.next - mapping->info.dir.first_dir_index) first_cluster += (s->directory.next - mapping->info.dir.first_dir_index)
* 0x20 / s->cluster_size; * 0x20 / s->cluster_size;
@ -819,12 +892,12 @@ static int read_directory(BDRVVVFATState* s, int mapping_index)
static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num) static inline uint32_t sector2cluster(BDRVVVFATState* s,off_t sector_num)
{ {
return (sector_num-s->faked_sectors)/s->sectors_per_cluster; return (sector_num - s->offset_to_root_dir) / s->sectors_per_cluster;
} }
static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num) static inline off_t cluster2sector(BDRVVVFATState* s, uint32_t cluster_num)
{ {
return s->faked_sectors + s->sectors_per_cluster * cluster_num; return s->offset_to_root_dir + s->sectors_per_cluster * cluster_num;
} }
static int init_directories(BDRVVVFATState* s, static int init_directories(BDRVVVFATState* s,
@ -851,6 +924,9 @@ static int init_directories(BDRVVVFATState* s,
i = 1+s->sectors_per_cluster*0x200*8/s->fat_type; i = 1+s->sectors_per_cluster*0x200*8/s->fat_type;
s->sectors_per_fat=(s->sector_count+i)/i; /* round up */ s->sectors_per_fat=(s->sector_count+i)/i; /* round up */
s->offset_to_fat = s->offset_to_bootsector + 1;
s->offset_to_root_dir = s->offset_to_fat + s->sectors_per_fat * 2;
array_init(&(s->mapping),sizeof(mapping_t)); array_init(&(s->mapping),sizeof(mapping_t));
array_init(&(s->directory),sizeof(direntry_t)); array_init(&(s->directory),sizeof(direntry_t));
@ -864,7 +940,8 @@ static int init_directories(BDRVVVFATState* s,
/* Now build FAT, and write back information into directory */ /* Now build FAT, and write back information into directory */
init_fat(s); init_fat(s);
s->faked_sectors=s->first_sectors_number+s->sectors_per_fat*2; /* TODO: if there are more entries, bootsector has to be adjusted! */
s->root_entries = 0x02 * 0x10 * s->sectors_per_cluster;
s->cluster_count=sector2cluster(s, s->sector_count); s->cluster_count=sector2cluster(s, s->sector_count);
mapping = array_get_next(&(s->mapping)); mapping = array_get_next(&(s->mapping));
@ -933,7 +1010,6 @@ static int init_directories(BDRVVVFATState* s,
} }
mapping = array_get(&(s->mapping), 0); mapping = array_get(&(s->mapping), 0);
s->sectors_of_root_directory = mapping->end * s->sectors_per_cluster;
s->last_cluster_of_root_directory = mapping->end; s->last_cluster_of_root_directory = mapping->end;
/* the FAT signature */ /* the FAT signature */
@ -942,34 +1018,37 @@ static int init_directories(BDRVVVFATState* s,
s->current_mapping = NULL; s->current_mapping = NULL;
bootsector=(bootsector_t*)(s->first_sectors+(s->first_sectors_number-1)*0x200); bootsector = (bootsector_t *)(s->first_sectors
+ s->offset_to_bootsector * 0x200);
bootsector->jump[0]=0xeb; bootsector->jump[0]=0xeb;
bootsector->jump[1]=0x3e; bootsector->jump[1]=0x3e;
bootsector->jump[2]=0x90; bootsector->jump[2]=0x90;
memcpy(bootsector->name,"QEMU ",8); memcpy(bootsector->name, "MSWIN4.1", 8);
bootsector->sector_size=cpu_to_le16(0x200); bootsector->sector_size=cpu_to_le16(0x200);
bootsector->sectors_per_cluster=s->sectors_per_cluster; bootsector->sectors_per_cluster=s->sectors_per_cluster;
bootsector->reserved_sectors=cpu_to_le16(1); bootsector->reserved_sectors=cpu_to_le16(1);
bootsector->number_of_fats=0x2; /* number of FATs */ bootsector->number_of_fats=0x2; /* number of FATs */
bootsector->root_entries=cpu_to_le16(s->sectors_of_root_directory*0x10); bootsector->root_entries = cpu_to_le16(s->root_entries);
bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count); bootsector->total_sectors16=s->sector_count>0xffff?0:cpu_to_le16(s->sector_count);
bootsector->media_type=(s->first_sectors_number>1?0xf8:0xf0); /* media descriptor (f8=hd, f0=3.5 fd)*/ /* media descriptor: hard disk=0xf8, floppy=0xf0 */
bootsector->media_type = (s->offset_to_bootsector > 0 ? 0xf8 : 0xf0);
s->fat.pointer[0] = bootsector->media_type; s->fat.pointer[0] = bootsector->media_type;
bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat); bootsector->sectors_per_fat=cpu_to_le16(s->sectors_per_fat);
bootsector->sectors_per_track = cpu_to_le16(secs); bootsector->sectors_per_track = cpu_to_le16(secs);
bootsector->number_of_heads = cpu_to_le16(heads); bootsector->number_of_heads = cpu_to_le16(heads);
bootsector->hidden_sectors=cpu_to_le32(s->first_sectors_number==1?0:0x3f); bootsector->hidden_sectors = cpu_to_le32(s->offset_to_bootsector);
bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0); bootsector->total_sectors=cpu_to_le32(s->sector_count>0xffff?s->sector_count:0);
/* LATER TODO: if FAT32, this is wrong */ /* LATER TODO: if FAT32, this is wrong */
bootsector->u.fat16.drive_number=s->first_sectors_number==1?0:0x80; /* fda=0, hda=0x80 */ /* drive_number: fda=0, hda=0x80 */
bootsector->u.fat16.current_head=0; bootsector->u.fat16.drive_number = s->offset_to_bootsector == 0 ? 0 : 0x80;
bootsector->u.fat16.signature=0x29; bootsector->u.fat16.signature=0x29;
bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd); bootsector->u.fat16.id=cpu_to_le32(0xfabe1afd);
memcpy(bootsector->u.fat16.volume_label, s->volume_label, memcpy(bootsector->u.fat16.volume_label, s->volume_label,
sizeof(bootsector->u.fat16.volume_label)); sizeof(bootsector->u.fat16.volume_label));
memcpy(bootsector->fat_type,(s->fat_type==12?"FAT12 ":s->fat_type==16?"FAT16 ":"FAT32 "),8); memcpy(bootsector->u.fat16.fat_type,
s->fat_type == 12 ? "FAT12 " : "FAT16 ", 8);
bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa; bootsector->magic[0]=0x55; bootsector->magic[1]=0xaa;
return 0; return 0;
@ -1119,7 +1198,6 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
secs = s->fat_type == 12 ? 18 : 36; secs = s->fat_type == 12 ? 18 : 36;
s->sectors_per_cluster = 1; s->sectors_per_cluster = 1;
} }
s->first_sectors_number = 1;
cyls = 80; cyls = 80;
heads = 2; heads = 2;
} else { } else {
@ -1127,7 +1205,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
if (!s->fat_type) { if (!s->fat_type) {
s->fat_type = 16; s->fat_type = 16;
} }
s->first_sectors_number = 0x40; s->offset_to_bootsector = 0x3f;
cyls = s->fat_type == 12 ? 64 : 1024; cyls = s->fat_type == 12 ? 64 : 1024;
heads = 16; heads = 16;
secs = 63; secs = 63;
@ -1163,7 +1241,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
fprintf(stderr, "vvfat %s chs %d,%d,%d\n", fprintf(stderr, "vvfat %s chs %d,%d,%d\n",
dirname, cyls, heads, secs); dirname, cyls, heads, secs);
s->sector_count = cyls * heads * secs - (s->first_sectors_number - 1); s->sector_count = cyls * heads * secs - s->offset_to_bootsector;
if (qemu_opt_get_bool(opts, "rw", false)) { if (qemu_opt_get_bool(opts, "rw", false)) {
if (!bdrv_is_read_only(bs)) { if (!bdrv_is_read_only(bs)) {
@ -1193,7 +1271,8 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
goto fail; goto fail;
} }
s->sector_count = s->faked_sectors + s->sectors_per_cluster*s->cluster_count; s->sector_count = s->offset_to_root_dir
+ s->sectors_per_cluster * s->cluster_count;
/* Disable migration when vvfat is used rw */ /* Disable migration when vvfat is used rw */
if (s->qcow) { if (s->qcow) {
@ -1209,7 +1288,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
} }
} }
if (s->first_sectors_number == 0x40) { if (s->offset_to_bootsector > 0) {
init_mbr(s, cyls, heads, secs); init_mbr(s, cyls, heads, secs);
} }
@ -1403,34 +1482,46 @@ static int vvfat_read(BlockDriverState *bs, int64_t sector_num,
if (sector_num >= bs->total_sectors) if (sector_num >= bs->total_sectors)
return -1; return -1;
if (s->qcow) { if (s->qcow) {
int n; int64_t n;
int ret; int ret;
ret = bdrv_is_allocated(s->qcow->bs, sector_num, ret = bdrv_is_allocated(s->qcow->bs, sector_num * BDRV_SECTOR_SIZE,
nb_sectors - i, &n); (nb_sectors - i) * BDRV_SECTOR_SIZE, &n);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
if (ret) { if (ret) {
DLOG(fprintf(stderr, "sectors %d+%d allocated\n", DLOG(fprintf(stderr, "sectors %" PRId64 "+%" PRId64
(int)sector_num, n)); " allocated\n", sector_num,
if (bdrv_read(s->qcow, sector_num, buf + i * 0x200, n)) { n >> BDRV_SECTOR_BITS));
if (bdrv_read(s->qcow, sector_num, buf + i * 0x200,
n >> BDRV_SECTOR_BITS)) {
return -1; return -1;
} }
i += n - 1; i += (n >> BDRV_SECTOR_BITS) - 1;
sector_num += n - 1; sector_num += (n >> BDRV_SECTOR_BITS) - 1;
continue; continue;
} }
DLOG(fprintf(stderr, "sector %d not allocated\n", (int)sector_num)); DLOG(fprintf(stderr, "sector %" PRId64 " not allocated\n",
sector_num));
}
if (sector_num < s->offset_to_root_dir) {
if (sector_num < s->offset_to_fat) {
memcpy(buf + i * 0x200,
&(s->first_sectors[sector_num * 0x200]),
0x200);
} else if (sector_num < s->offset_to_fat + s->sectors_per_fat) {
memcpy(buf + i * 0x200,
&(s->fat.pointer[(sector_num
- s->offset_to_fat) * 0x200]),
0x200);
} else if (sector_num < s->offset_to_root_dir) {
memcpy(buf + i * 0x200,
&(s->fat.pointer[(sector_num - s->offset_to_fat
- s->sectors_per_fat) * 0x200]),
0x200);
} }
if(sector_num<s->faked_sectors) {
if(sector_num<s->first_sectors_number)
memcpy(buf+i*0x200,&(s->first_sectors[sector_num*0x200]),0x200);
else if(sector_num-s->first_sectors_number<s->sectors_per_fat)
memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number)*0x200]),0x200);
else if(sector_num-s->first_sectors_number-s->sectors_per_fat<s->sectors_per_fat)
memcpy(buf+i*0x200,&(s->fat.pointer[(sector_num-s->first_sectors_number-s->sectors_per_fat)*0x200]),0x200);
} else { } else {
uint32_t sector=sector_num-s->faked_sectors, uint32_t sector = sector_num - s->offset_to_root_dir,
sector_offset_in_cluster=(sector%s->sectors_per_cluster), sector_offset_in_cluster=(sector%s->sectors_per_cluster),
cluster_num=sector/s->sectors_per_cluster; cluster_num=sector/s->sectors_per_cluster;
if(cluster_num > s->cluster_count || read_cluster(s, cluster_num) != 0) { if(cluster_num > s->cluster_count || read_cluster(s, cluster_num) != 0) {
@ -1657,6 +1748,9 @@ static int parse_short_name(BDRVVVFATState* s,
} else } else
lfn->name[i + j + 1] = '\0'; lfn->name[i + j + 1] = '\0';
if (lfn->name[0] == 0x05) {
lfn->name[0] = 0xe5;
}
lfn->len = strlen((char*)lfn->name); lfn->len = strlen((char*)lfn->name);
return 0; return 0;
@ -1688,7 +1782,7 @@ static inline bool cluster_was_modified(BDRVVVFATState *s,
uint32_t cluster_num) uint32_t cluster_num)
{ {
int was_modified = 0; int was_modified = 0;
int i, dummy; int i;
if (s->qcow == NULL) { if (s->qcow == NULL) {
return 0; return 0;
@ -1696,8 +1790,9 @@ static inline bool cluster_was_modified(BDRVVVFATState *s,
for (i = 0; !was_modified && i < s->sectors_per_cluster; i++) { for (i = 0; !was_modified && i < s->sectors_per_cluster; i++) {
was_modified = bdrv_is_allocated(s->qcow->bs, was_modified = bdrv_is_allocated(s->qcow->bs,
cluster2sector(s, cluster_num) + i, (cluster2sector(s, cluster_num) +
1, &dummy); i) * BDRV_SECTOR_SIZE,
BDRV_SECTOR_SIZE, NULL);
} }
/* /*
@ -1844,7 +1939,7 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
} }
if (copy_it) { if (copy_it) {
int i, dummy; int i;
/* /*
* This is horribly inefficient, but that is okay, since * This is horribly inefficient, but that is okay, since
* it is rarely executed, if at all. * it is rarely executed, if at all.
@ -1855,7 +1950,9 @@ static uint32_t get_cluster_count_for_direntry(BDRVVVFATState* s,
for (i = 0; i < s->sectors_per_cluster; i++) { for (i = 0; i < s->sectors_per_cluster; i++) {
int res; int res;
res = bdrv_is_allocated(s->qcow->bs, offset + i, 1, &dummy); res = bdrv_is_allocated(s->qcow->bs,
(offset + i) * BDRV_SECTOR_SIZE,
BDRV_SECTOR_SIZE, NULL);
if (res < 0) { if (res < 0) {
return -1; return -1;
} }
@ -2042,7 +2139,7 @@ DLOG(checkpoint());
memcpy(s->fat2, s->fat.pointer, size); memcpy(s->fat2, s->fat.pointer, size);
} }
check = vvfat_read(s->bs, check = vvfat_read(s->bs,
s->first_sectors_number, s->fat2, s->sectors_per_fat); s->offset_to_fat, s->fat2, s->sectors_per_fat);
if (check) { if (check) {
fprintf(stderr, "Could not copy fat\n"); fprintf(stderr, "Could not copy fat\n");
return 0; return 0;
@ -2861,7 +2958,7 @@ DLOG(checkpoint());
* - do not allow to write non-ASCII filenames * - do not allow to write non-ASCII filenames
*/ */
if (sector_num < s->first_sectors_number) if (sector_num < s->offset_to_fat)
return -1; return -1;
for (i = sector2cluster(s, sector_num); for (i = sector2cluster(s, sector_num);
@ -2968,8 +3065,7 @@ vvfat_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
static int64_t coroutine_fn vvfat_co_get_block_status(BlockDriverState *bs, static int64_t coroutine_fn vvfat_co_get_block_status(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int *n, BlockDriverState **file) int64_t sector_num, int nb_sectors, int *n, BlockDriverState **file)
{ {
BDRVVVFATState* s = bs->opaque; *n = bs->total_sectors - sector_num;
*n = s->sector_count - sector_num;
if (*n > nb_sectors) { if (*n > nb_sectors) {
*n = nb_sectors; *n = nb_sectors;
} else if (*n < 0) { } else if (*n < 0) {

View File

@ -50,6 +50,7 @@
#include "qmp-commands.h" #include "qmp-commands.h"
#include "block/trace.h" #include "block/trace.h"
#include "sysemu/arch_init.h" #include "sysemu/arch_init.h"
#include "sysemu/qtest.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "qemu/help_option.h" #include "qemu/help_option.h"
#include "qemu/throttle-options.h" #include "qemu/throttle-options.h"
@ -798,6 +799,9 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
const char *filename; const char *filename;
Error *local_err = NULL; Error *local_err = NULL;
int i; int i;
const char *deprecated[] = {
"serial", "trans", "secs", "heads", "cyls", "addr"
};
/* Change legacy command line options into QMP ones */ /* Change legacy command line options into QMP ones */
static const struct { static const struct {
@ -881,6 +885,16 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
"update your scripts.\n"); "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 */ /* Media type */
value = qemu_opt_get(legacy_opts, "media"); value = qemu_opt_get(legacy_opts, "media");
if (value) { if (value) {

View File

@ -132,9 +132,9 @@ typedef struct HDGeometry {
* BDRV_BLOCK_EOF: the returned pnum covers through end of file for this layer * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this layer
* *
* Internal flag: * Internal flag:
* BDRV_BLOCK_RAW: used internally to indicate that the request was * BDRV_BLOCK_RAW: for use by passthrough drivers, such as raw, to request
* answered by a passthrough driver such as raw and that the * that the block layer recompute the answer from the returned
* block layer should recompute the answer from bs->file. * BDS; must be accompanied by just BDRV_BLOCK_OFFSET_VALID.
* *
* If BDRV_BLOCK_OFFSET_VALID is set, bits 9-62 (BDRV_BLOCK_OFFSET_MASK) * 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 * 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, int64_t sector_num,
int nb_sectors, int *pnum, int nb_sectors, int *pnum,
BlockDriverState **file); BlockDriverState **file);
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int bdrv_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes,
int *pnum); int64_t *pnum);
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, 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_read_only(BlockDriverState *bs);
bool bdrv_is_writable(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_flags(BlockDriverState *bs);
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs); 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, void bdrv_round_to_clusters(BlockDriverState *bs,
int64_t offset, unsigned int bytes, int64_t offset, unsigned int bytes,
int64_t *cluster_offset, int64_t *cluster_offset,

View File

@ -21,17 +21,16 @@
#include "block/block_int.h" #include "block/block_int.h"
typedef struct CowRequest { typedef struct CowRequest {
int64_t start; int64_t start_byte;
int64_t end; int64_t end_byte;
QLIST_ENTRY(CowRequest) list; QLIST_ENTRY(CowRequest) list;
CoQueue wait_queue; /* coroutines blocked on this request */ CoQueue wait_queue; /* coroutines blocked on this request */
} CowRequest; } CowRequest;
void backup_wait_for_overlapping_requests(BlockJob *job, int64_t sector_num, void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset,
int nb_sectors); uint64_t bytes);
void backup_cow_request_begin(CowRequest *req, BlockJob *job, void backup_cow_request_begin(CowRequest *req, BlockJob *job,
int64_t sector_num, int64_t offset, uint64_t bytes);
int nb_sectors);
void backup_cow_request_end(CowRequest *req); void backup_cow_request_end(CowRequest *req);
void backup_do_checkpoint(BlockJob *job, Error **errp); void backup_do_checkpoint(BlockJob *job, Error **errp);

View File

@ -24,7 +24,8 @@ typedef struct {
/** Calculate and return delay for next request in ns /** 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 * 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 * return the amount of time (in ns) until the start of the next time
* slice that will permit sending the next chunk of data. * slice that will permit sending the next chunk of data.

View File

@ -34,7 +34,7 @@
#define BLK_MIG_FLAG_PROGRESS 0x04 #define BLK_MIG_FLAG_PROGRESS 0x04
#define BLK_MIG_FLAG_ZERO_BLOCK 0x08 #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 #define MAX_INFLIGHT_IO 512
@ -267,16 +267,20 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds)
BlockBackend *bb = bmds->blk; BlockBackend *bb = bmds->blk;
BlkMigBlock *blk; BlkMigBlock *blk;
int nr_sectors; int nr_sectors;
int64_t count;
if (bmds->shared_base) { if (bmds->shared_base) {
qemu_mutex_lock_iothread(); qemu_mutex_lock_iothread();
aio_context_acquire(blk_get_aio_context(bb)); aio_context_acquire(blk_get_aio_context(bb));
/* Skip unallocated sectors; intentionally treats failure as /* Skip unallocated sectors; intentionally treats failure or
* an allocated sector */ * partial sector as an allocated sector */
while (cur_sector < total_sectors && while (cur_sector < total_sectors &&
!bdrv_is_allocated(blk_bs(bb), cur_sector, !bdrv_is_allocated(blk_bs(bb), cur_sector * BDRV_SECTOR_SIZE,
MAX_IS_ALLOCATED_SEARCH, &nr_sectors)) { MAX_IS_ALLOCATED_SEARCH, &count)) {
cur_sector += nr_sectors; if (count < BDRV_SECTOR_SIZE) {
break;
}
cur_sector += count >> BDRV_SECTOR_BITS;
} }
aio_context_release(blk_get_aio_context(bb)); aio_context_release(blk_get_aio_context(bb));
qemu_mutex_unlock_iothread(); qemu_mutex_unlock_iothread();

View File

@ -464,7 +464,7 @@ static int img_create(int argc, char **argv)
{"object", required_argument, 0, OPTION_OBJECT}, {"object", required_argument, 0, OPTION_OBJECT},
{0, 0, 0, 0} {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); long_options, NULL);
if (c == -1) { if (c == -1) {
break; break;
@ -488,14 +488,6 @@ static int img_create(int argc, char **argv)
case 'f': case 'f':
fmt = optarg; fmt = optarg;
break; 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': case 'o':
if (!is_valid_option_list(optarg)) { if (!is_valid_option_list(optarg)) {
error_report("Invalid option list: %s", optarg); error_report("Invalid option list: %s", optarg);
@ -1516,12 +1508,16 @@ static int img_compare(int argc, char **argv)
} }
for (;;) { for (;;) {
int64_t count;
nb_sectors = sectors_to_process(total_sectors_over, sector_num); nb_sectors = sectors_to_process(total_sectors_over, sector_num);
if (nb_sectors <= 0) { if (nb_sectors <= 0) {
break; break;
} }
ret = bdrv_is_allocated_above(blk_bs(blk_over), NULL, sector_num, ret = bdrv_is_allocated_above(blk_bs(blk_over), NULL,
nb_sectors, &pnum); sector_num * BDRV_SECTOR_SIZE,
nb_sectors * BDRV_SECTOR_SIZE,
&count);
if (ret < 0) { if (ret < 0) {
ret = 3; ret = 3;
error_report("Sector allocation test failed for %s", error_report("Sector allocation test failed for %s",
@ -1529,7 +1525,10 @@ static int img_compare(int argc, char **argv)
goto out; 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) { if (ret) {
ret = check_empty_sectors(blk_over, sector_num, nb_sectors, ret = check_empty_sectors(blk_over, sector_num, nb_sectors,
filename_over, buf1, quiet); 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}, {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS},
{0, 0, 0, 0} {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); long_options, NULL);
if (c == -1) { if (c == -1) {
break; break;
@ -2012,14 +2011,6 @@ static int img_convert(int argc, char **argv)
case 'c': case 'c':
s.compressed = true; s.compressed = true;
break; 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': case 'o':
if (!is_valid_option_list(optarg)) { if (!is_valid_option_list(optarg)) {
error_report("Invalid option list: %s", 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; int64_t new_backing_num_sectors = 0;
uint64_t sector; uint64_t sector;
int n; int n;
int64_t count;
float local_progress = 0; float local_progress = 0;
buf_old = blk_blockalign(blk, IO_BUF_SIZE); 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 */ /* 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) { if (ret < 0) {
error_report("error while reading image metadata: %s", error_report("error while reading image metadata: %s",
strerror(-ret)); strerror(-ret));
goto out; 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) { if (ret) {
continue; continue;
} }

View File

@ -1760,12 +1760,12 @@ out:
static int alloc_f(BlockBackend *blk, int argc, char **argv) static int alloc_f(BlockBackend *blk, int argc, char **argv)
{ {
BlockDriverState *bs = blk_bs(blk); BlockDriverState *bs = blk_bs(blk);
int64_t offset, sector_num, nb_sectors, remaining, count; int64_t offset, start, remaining, count;
char s1[64]; char s1[64];
int num, ret; int ret;
int64_t sum_alloc; int64_t num, sum_alloc;
offset = cvtnum(argv[1]); start = offset = cvtnum(argv[1]);
if (offset < 0) { if (offset < 0) {
print_cvtnum_err(offset, argv[1]); print_cvtnum_err(offset, argv[1]);
return 0; return 0;
@ -1793,32 +1793,30 @@ static int alloc_f(BlockBackend *blk, int argc, char **argv)
count); count);
return 0; return 0;
} }
nb_sectors = count >> BDRV_SECTOR_BITS;
remaining = nb_sectors; remaining = count;
sum_alloc = 0; sum_alloc = 0;
sector_num = offset >> 9;
while (remaining) { while (remaining) {
ret = bdrv_is_allocated(bs, sector_num, remaining, &num); ret = bdrv_is_allocated(bs, offset, remaining, &num);
if (ret < 0) { if (ret < 0) {
printf("is_allocated failed: %s\n", strerror(-ret)); printf("is_allocated failed: %s\n", strerror(-ret));
return 0; return 0;
} }
sector_num += num; offset += num;
remaining -= num; remaining -= num;
if (ret) { if (ret) {
sum_alloc += num; sum_alloc += num;
} }
if (num == 0) { if (num == 0) {
nb_sectors -= remaining; count -= remaining;
remaining = 0; remaining = 0;
} }
} }
cvtstr(offset, s1, sizeof(s1)); cvtstr(start, s1, sizeof(s1));
printf("%"PRId64"/%"PRId64" bytes allocated at offset %s\n", 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; return 0;
} }
@ -1833,14 +1831,15 @@ static const cmdinfo_t alloc_cmd = {
}; };
static int map_is_allocated(BlockDriverState *bs, int64_t sector_num, static int map_is_allocated(BlockDriverState *bs, int64_t offset,
int64_t nb_sectors, int64_t *pnum) int64_t bytes, int64_t *pnum)
{ {
int num, num_checked; int64_t num;
int num_checked;
int ret, firstret; int ret, firstret;
num_checked = MIN(nb_sectors, INT_MAX); num_checked = MIN(bytes, BDRV_REQUEST_MAX_BYTES);
ret = bdrv_is_allocated(bs, sector_num, num_checked, &num); ret = bdrv_is_allocated(bs, offset, num_checked, &num);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -1848,12 +1847,12 @@ static int map_is_allocated(BlockDriverState *bs, int64_t sector_num,
firstret = ret; firstret = ret;
*pnum = num; *pnum = num;
while (nb_sectors > 0 && ret == firstret) { while (bytes > 0 && ret == firstret) {
sector_num += num; offset += num;
nb_sectors -= num; bytes -= num;
num_checked = MIN(nb_sectors, INT_MAX); num_checked = MIN(bytes, BDRV_REQUEST_MAX_BYTES);
ret = bdrv_is_allocated(bs, sector_num, num_checked, &num); ret = bdrv_is_allocated(bs, offset, num_checked, &num);
if (ret == firstret && num) { if (ret == firstret && num) {
*pnum += num; *pnum += num;
} else { } 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) static int map_f(BlockBackend *blk, int argc, char **argv)
{ {
int64_t offset; int64_t offset, bytes;
int64_t nb_sectors, total_sectors;
char s1[64], s2[64]; char s1[64], s2[64];
int64_t num; int64_t num;
int ret; int ret;
const char *retstr; const char *retstr;
offset = 0; offset = 0;
total_sectors = blk_nb_sectors(blk); bytes = blk_getlength(blk);
if (total_sectors < 0) { if (bytes < 0) {
error_report("Failed to query image length: %s", error_report("Failed to query image length: %s", strerror(-bytes));
strerror(-total_sectors));
return 0; return 0;
} }
nb_sectors = total_sectors; while (bytes) {
ret = map_is_allocated(blk_bs(blk), offset, bytes, &num);
do {
ret = map_is_allocated(blk_bs(blk), offset, nb_sectors, &num);
if (ret < 0) { if (ret < 0) {
error_report("Failed to get allocation status: %s", strerror(-ret)); error_report("Failed to get allocation status: %s", strerror(-ret));
return 0; return 0;
@ -1894,15 +1889,14 @@ static int map_f(BlockBackend *blk, int argc, char **argv)
} }
retstr = ret ? " allocated" : "not allocated"; retstr = ret ? " allocated" : "not allocated";
cvtstr(num << BDRV_SECTOR_BITS, s1, sizeof(s1)); cvtstr(num, s1, sizeof(s1));
cvtstr(offset << BDRV_SECTOR_BITS, s2, sizeof(s2)); cvtstr(offset, s2, sizeof(s2));
printf("%s (0x%" PRIx64 ") bytes %s at offset %s (0x%" PRIx64 ")\n", printf("%s (0x%" PRIx64 ") bytes %s at offset %s (0x%" PRIx64 ")\n",
s1, num << BDRV_SECTOR_BITS, retstr, s1, num, retstr, s2, offset);
s2, offset << BDRV_SECTOR_BITS);
offset += num; offset += num;
nb_sectors -= num; bytes -= num;
} while (offset < total_sectors); }
return 0; return 0;
} }

View File

@ -230,13 +230,14 @@ static int open_f(BlockBackend *blk, int argc, char **argv)
qemu_opts_reset(&empty_opts); qemu_opts_reset(&empty_opts);
if (optind == argc - 1) { 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) { } else if (optind == argc) {
return openfile(NULL, flags, writethrough, force_share, opts); openfile(NULL, flags, writethrough, force_share, opts);
} else { } else {
QDECREF(opts); 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) static int quit_f(BlockBackend *blk, int argc, char **argv)

View File

@ -818,6 +818,8 @@ of available connectors of a given interface type.
This option defines the type of the media: disk or cdrom. This option defines the type of the media: disk or cdrom.
@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}] @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 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} @item snapshot=@var{snapshot}
@var{snapshot} is "on" or "off" and controls snapshot mode for the given drive @var{snapshot} is "on" or "off" and controls snapshot mode for the given drive
(see @option{-snapshot}). (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 the format. Can be used to specify format=raw to avoid interpreting
an untrusted format header. an untrusted format header.
@item serial=@var{serial} @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} @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} @item werror=@var{action},rerror=@var{action}
Specify which @var{action} to take on write and read errors. Valid actions are: 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), "ignore" (ignore the error and try to continue), "stop" (pause QEMU),

View File

@ -21,6 +21,7 @@ Format specific information:
refcount bits: 16 refcount bits: 16
corrupt: true corrupt: true
can't open device TEST_DIR/t.IMGFMT: IMGFMT: Image is corrupt; cannot be opened read/write 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 read 512/512 bytes at offset 0
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)

View File

@ -8,6 +8,7 @@ cluster_size: 65536
backing file: TEST_DIR/t.IMGFMT.base backing file: TEST_DIR/t.IMGFMT.base
backing file format: foo backing file format: foo
can't open device TEST_DIR/t.qcow2: Could not open backing file: Unknown driver '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 read 4096/4096 bytes at offset 0
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done *** done

View File

@ -33,10 +33,12 @@ Is another process using the image?
_qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512 _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 can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
Is another process using the image? 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 _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 can't open device TEST_DIR/t.qcow2: Failed to get shared "write" lock
Is another process using the image? Is another process using the image?
no file open, try 'help open'
_qemu_img_wrapper info TEST_DIR/t.qcow2 _qemu_img_wrapper info TEST_DIR/t.qcow2
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock 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 _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 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 _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 _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 can't open device TEST_DIR/t.qcow2: Failed to get "write" lock
Is another process using the image? 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 _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 _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 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 _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 _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 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 _qemu_io_wrapper -c open -r -U TEST_DIR/t.qcow2 -c read 0 512

View File

@ -43,6 +43,7 @@ _supported_proto file
CLUSTER_SIZE=1M CLUSTER_SIZE=1M
size=128M size=128M
options=driver=blkdebug,image.driver=qcow2 options=driver=blkdebug,image.driver=qcow2
nested_opts=image.file.driver=file,image.file.filename=$TEST_IMG
echo echo
echo "== setting up files ==" echo "== setting up files =="
@ -106,6 +107,8 @@ function verify_io()
} }
verify_io | $QEMU_IO -r "$TEST_IMG" | _filter_qemu_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 _check_test_img

View File

@ -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) 29 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 23068672/23068672 bytes at offset 111149056 read 23068672/23068672 bytes at offset 111149056
22 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) 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. No errors were found on the image.
*** done *** done