Block patches for 7.0-rc0:
- New fleecing backup scheme - iotest fixes - Fixes for the curl block driver - Fix for the preallocate block driver - IDE fix for zero-length TRIM requests -----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEEy2LXoO44KeRfAE00ofpA0JgBnN8FAmIl33sSHGhyZWl0ekBy ZWRoYXQuY29tAAoJEKH6QNCYAZzfD60P/0xXwKmXiyq7GkoVdkZaI4ErMn29rnzl APoM6Q3GbMII6ipkqVIFM2BIhGDCl8X+KbzvDTm23jvXR7SAnjel+GBcFYTZ4/3r LbS/mW3SGPjEG6CldXjHDxD/k+3sw1wXOGW30+PhEkpokNSJKh3evsNxUA+liPQT MqrNWilh28S4CMM2YnHEJNX/e0WujYagE0PN4UKrr/+bw8mN+UrirCC/BwH0RsAz H+OVek/SaGUer4MAXNH+LVWwm3/F+sZQBqyLZc0ORavKnXEPlf/fKbGx5+IkV2ty h/GX+oxLSsfnXDVhqvP71RbSkLIOCsrSPzTqz3vicBmwtqaIzjor7wVS/vJlK1i5 cVEYa2te3Ccbpijb9VOBwTu+t8YB8x/Ot8S22p++uC0CY/Gf1paCe8uNqXHQYhfD Sv2Xw9vQsCIvB+7vmJ+iMSf5LFPO0I4Hh7zil6ecKgtxtkVLtqYa7t1UE/5cOUtO mF3mzS7bQhcU569z3+hZEBiCrH+rSNvHvnnAV/IFN4Y1PqSBk6uwoqHzUFZQq6Dr 6IdduqUmGs9ooDZuxKgShr81Sn18T6BkawcsQMMHOI4gKRik3l7kxUnPeTIYWi2T j0ihaZyH5rRfa80CBnN18Q+90buYb0OafKZ9y4/RDOScoHeR8Yx/RjUwKpn+6yGI TOUr+3PvGOPl =OH/S -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/hreitz-gitlab/tags/pull-block-2022-03-07' into staging Block patches for 7.0-rc0: - New fleecing backup scheme - iotest fixes - Fixes for the curl block driver - Fix for the preallocate block driver - IDE fix for zero-length TRIM requests # gpg: Signature made Mon 07 Mar 2022 10:33:31 GMT # gpg: using RSA key CB62D7A0EE3829E45F004D34A1FA40D098019CDF # gpg: issuer "hreitz@redhat.com" # gpg: Good signature from "Hanna Reitz <hreitz@redhat.com>" [marginal] # gpg: WARNING: This key is not certified with sufficiently trusted signatures! # gpg: It is not certain that the signature belongs to the owner. # Primary key fingerprint: CB62 D7A0 EE38 29E4 5F00 4D34 A1FA 40D0 9801 9CDF * remotes/hreitz-gitlab/tags/pull-block-2022-03-07: (23 commits) iotests/image-fleecing: test push backup with fleecing iotests/image-fleecing: add test case with bitmap iotests.py: add qemu_io_pipe_and_status() iotests/image-fleecing: add test-case for fleecing format node block: copy-before-write: realize snapshot-access API block: introduce snapshot-access block driver block/io: introduce block driver snapshot-access API block/reqlist: add reqlist_wait_all() block/dirty-bitmap: introduce bdrv_dirty_bitmap_status() block/reqlist: reqlist_find_conflict(): use ranges_overlap() block: intoduce reqlist block/block-copy: add block_copy_reset() block/copy-before-write: add bitmap open parameter block/block-copy: block_copy_state_new(): add bitmap parameter block/dirty-bitmap: bdrv_merge_dirty_bitmap(): add return value block/block-copy: move copy_bitmap initialization to block_copy_state_new() iotests: Write test output to TEST_DIR tests/qemu-iotests/testrunner: Quote "case not run" lines in TAP mode tests/qemu-iotests/040: Skip TestCommitWithFilters without 'throttle' block: fix preallocate filter: don't do unaligned preallocate requests ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
b49872aa8f
@ -2515,9 +2515,12 @@ F: block/stream.c
|
|||||||
F: block/mirror.c
|
F: block/mirror.c
|
||||||
F: qapi/job.json
|
F: qapi/job.json
|
||||||
F: block/block-copy.c
|
F: block/block-copy.c
|
||||||
F: include/block/block-copy.c
|
F: include/block/block-copy.h
|
||||||
|
F: block/reqlist.c
|
||||||
|
F: include/block/reqlist.h
|
||||||
F: block/copy-before-write.h
|
F: block/copy-before-write.h
|
||||||
F: block/copy-before-write.c
|
F: block/copy-before-write.c
|
||||||
|
F: block/snapshot-access.c
|
||||||
F: include/block/aio_task.h
|
F: include/block/aio_task.h
|
||||||
F: block/aio_task.c
|
F: block/aio_task.c
|
||||||
F: util/qemu-co-shared-resource.c
|
F: util/qemu-co-shared-resource.c
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "block/block-copy.h"
|
#include "block/block-copy.h"
|
||||||
|
#include "block/reqlist.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/units.h"
|
#include "qemu/units.h"
|
||||||
#include "qemu/coroutine.h"
|
#include "qemu/coroutine.h"
|
||||||
@ -83,7 +84,6 @@ typedef struct BlockCopyTask {
|
|||||||
*/
|
*/
|
||||||
BlockCopyState *s;
|
BlockCopyState *s;
|
||||||
BlockCopyCallState *call_state;
|
BlockCopyCallState *call_state;
|
||||||
int64_t offset;
|
|
||||||
/*
|
/*
|
||||||
* @method can also be set again in the while loop of
|
* @method can also be set again in the while loop of
|
||||||
* block_copy_dirty_clusters(), but it is never accessed concurrently
|
* block_copy_dirty_clusters(), but it is never accessed concurrently
|
||||||
@ -94,21 +94,17 @@ typedef struct BlockCopyTask {
|
|||||||
BlockCopyMethod method;
|
BlockCopyMethod method;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fields whose state changes throughout the execution
|
* Generally, req is protected by lock in BlockCopyState, Still req.offset
|
||||||
* Protected by lock in BlockCopyState.
|
* is only set on task creation, so may be read concurrently after creation.
|
||||||
|
* req.bytes is changed at most once, and need only protecting the case of
|
||||||
|
* parallel read while updating @bytes value in block_copy_task_shrink().
|
||||||
*/
|
*/
|
||||||
CoQueue wait_queue; /* coroutines blocked on this task */
|
BlockReq req;
|
||||||
/*
|
|
||||||
* Only protect the case of parallel read while updating @bytes
|
|
||||||
* value in block_copy_task_shrink().
|
|
||||||
*/
|
|
||||||
int64_t bytes;
|
|
||||||
QLIST_ENTRY(BlockCopyTask) list;
|
|
||||||
} BlockCopyTask;
|
} BlockCopyTask;
|
||||||
|
|
||||||
static int64_t task_end(BlockCopyTask *task)
|
static int64_t task_end(BlockCopyTask *task)
|
||||||
{
|
{
|
||||||
return task->offset + task->bytes;
|
return task->req.offset + task->req.bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct BlockCopyState {
|
typedef struct BlockCopyState {
|
||||||
@ -136,7 +132,7 @@ typedef struct BlockCopyState {
|
|||||||
CoMutex lock;
|
CoMutex lock;
|
||||||
int64_t in_flight_bytes;
|
int64_t in_flight_bytes;
|
||||||
BlockCopyMethod method;
|
BlockCopyMethod method;
|
||||||
QLIST_HEAD(, BlockCopyTask) tasks; /* All tasks from all block-copy calls */
|
BlockReqList reqs;
|
||||||
QLIST_HEAD(, BlockCopyCallState) calls;
|
QLIST_HEAD(, BlockCopyCallState) calls;
|
||||||
/*
|
/*
|
||||||
* skip_unallocated:
|
* skip_unallocated:
|
||||||
@ -160,42 +156,6 @@ typedef struct BlockCopyState {
|
|||||||
RateLimit rate_limit;
|
RateLimit rate_limit;
|
||||||
} BlockCopyState;
|
} BlockCopyState;
|
||||||
|
|
||||||
/* Called with lock held */
|
|
||||||
static BlockCopyTask *find_conflicting_task(BlockCopyState *s,
|
|
||||||
int64_t offset, int64_t bytes)
|
|
||||||
{
|
|
||||||
BlockCopyTask *t;
|
|
||||||
|
|
||||||
QLIST_FOREACH(t, &s->tasks, list) {
|
|
||||||
if (offset + bytes > t->offset && offset < t->offset + t->bytes) {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If there are no intersecting tasks return false. Otherwise, wait for the
|
|
||||||
* first found intersecting tasks to finish and return true.
|
|
||||||
*
|
|
||||||
* Called with lock held. May temporary release the lock.
|
|
||||||
* Return value of 0 proves that lock was NOT released.
|
|
||||||
*/
|
|
||||||
static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
|
|
||||||
int64_t bytes)
|
|
||||||
{
|
|
||||||
BlockCopyTask *task = find_conflicting_task(s, offset, bytes);
|
|
||||||
|
|
||||||
if (!task) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
qemu_co_queue_wait(&task->wait_queue, &s->lock);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called with lock held */
|
/* Called with lock held */
|
||||||
static int64_t block_copy_chunk_size(BlockCopyState *s)
|
static int64_t block_copy_chunk_size(BlockCopyState *s)
|
||||||
{
|
{
|
||||||
@ -239,7 +199,7 @@ block_copy_task_create(BlockCopyState *s, BlockCopyCallState *call_state,
|
|||||||
bytes = QEMU_ALIGN_UP(bytes, s->cluster_size);
|
bytes = QEMU_ALIGN_UP(bytes, s->cluster_size);
|
||||||
|
|
||||||
/* region is dirty, so no existent tasks possible in it */
|
/* region is dirty, so no existent tasks possible in it */
|
||||||
assert(!find_conflicting_task(s, offset, bytes));
|
assert(!reqlist_find_conflict(&s->reqs, offset, bytes));
|
||||||
|
|
||||||
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
|
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
|
||||||
s->in_flight_bytes += bytes;
|
s->in_flight_bytes += bytes;
|
||||||
@ -249,12 +209,9 @@ block_copy_task_create(BlockCopyState *s, BlockCopyCallState *call_state,
|
|||||||
.task.func = block_copy_task_entry,
|
.task.func = block_copy_task_entry,
|
||||||
.s = s,
|
.s = s,
|
||||||
.call_state = call_state,
|
.call_state = call_state,
|
||||||
.offset = offset,
|
|
||||||
.bytes = bytes,
|
|
||||||
.method = s->method,
|
.method = s->method,
|
||||||
};
|
};
|
||||||
qemu_co_queue_init(&task->wait_queue);
|
reqlist_init_req(&s->reqs, &task->req, offset, bytes);
|
||||||
QLIST_INSERT_HEAD(&s->tasks, task, list);
|
|
||||||
|
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
@ -270,34 +227,34 @@ static void coroutine_fn block_copy_task_shrink(BlockCopyTask *task,
|
|||||||
int64_t new_bytes)
|
int64_t new_bytes)
|
||||||
{
|
{
|
||||||
QEMU_LOCK_GUARD(&task->s->lock);
|
QEMU_LOCK_GUARD(&task->s->lock);
|
||||||
if (new_bytes == task->bytes) {
|
if (new_bytes == task->req.bytes) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(new_bytes > 0 && new_bytes < task->bytes);
|
assert(new_bytes > 0 && new_bytes < task->req.bytes);
|
||||||
|
|
||||||
task->s->in_flight_bytes -= task->bytes - new_bytes;
|
task->s->in_flight_bytes -= task->req.bytes - new_bytes;
|
||||||
bdrv_set_dirty_bitmap(task->s->copy_bitmap,
|
bdrv_set_dirty_bitmap(task->s->copy_bitmap,
|
||||||
task->offset + new_bytes, task->bytes - new_bytes);
|
task->req.offset + new_bytes,
|
||||||
|
task->req.bytes - new_bytes);
|
||||||
|
|
||||||
task->bytes = new_bytes;
|
reqlist_shrink_req(&task->req, new_bytes);
|
||||||
qemu_co_queue_restart_all(&task->wait_queue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void coroutine_fn block_copy_task_end(BlockCopyTask *task, int ret)
|
static void coroutine_fn block_copy_task_end(BlockCopyTask *task, int ret)
|
||||||
{
|
{
|
||||||
QEMU_LOCK_GUARD(&task->s->lock);
|
QEMU_LOCK_GUARD(&task->s->lock);
|
||||||
task->s->in_flight_bytes -= task->bytes;
|
task->s->in_flight_bytes -= task->req.bytes;
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_set_dirty_bitmap(task->s->copy_bitmap, task->offset, task->bytes);
|
bdrv_set_dirty_bitmap(task->s->copy_bitmap, task->req.offset,
|
||||||
|
task->req.bytes);
|
||||||
}
|
}
|
||||||
QLIST_REMOVE(task, list);
|
|
||||||
if (task->s->progress) {
|
if (task->s->progress) {
|
||||||
progress_set_remaining(task->s->progress,
|
progress_set_remaining(task->s->progress,
|
||||||
bdrv_get_dirty_count(task->s->copy_bitmap) +
|
bdrv_get_dirty_count(task->s->copy_bitmap) +
|
||||||
task->s->in_flight_bytes);
|
task->s->in_flight_bytes);
|
||||||
}
|
}
|
||||||
qemu_co_queue_restart_all(&task->wait_queue);
|
reqlist_remove_req(&task->req);
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_copy_state_free(BlockCopyState *s)
|
void block_copy_state_free(BlockCopyState *s)
|
||||||
@ -384,8 +341,10 @@ static int64_t block_copy_calculate_cluster_size(BlockDriverState *target,
|
|||||||
}
|
}
|
||||||
|
|
||||||
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||||
|
const BdrvDirtyBitmap *bitmap,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
|
ERRP_GUARD();
|
||||||
BlockCopyState *s;
|
BlockCopyState *s;
|
||||||
int64_t cluster_size;
|
int64_t cluster_size;
|
||||||
BdrvDirtyBitmap *copy_bitmap;
|
BdrvDirtyBitmap *copy_bitmap;
|
||||||
@ -402,6 +361,17 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
bdrv_disable_dirty_bitmap(copy_bitmap);
|
bdrv_disable_dirty_bitmap(copy_bitmap);
|
||||||
|
if (bitmap) {
|
||||||
|
if (!bdrv_merge_dirty_bitmap(copy_bitmap, bitmap, NULL, errp)) {
|
||||||
|
error_prepend(errp, "Failed to merge bitmap '%s' to internal "
|
||||||
|
"copy-bitmap: ", bdrv_dirty_bitmap_name(bitmap));
|
||||||
|
bdrv_release_dirty_bitmap(copy_bitmap);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bdrv_set_dirty_bitmap(copy_bitmap, 0,
|
||||||
|
bdrv_dirty_bitmap_size(copy_bitmap));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If source is in backing chain of target assume that target is going to be
|
* If source is in backing chain of target assume that target is going to be
|
||||||
@ -437,7 +407,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
|||||||
|
|
||||||
ratelimit_init(&s->rate_limit);
|
ratelimit_init(&s->rate_limit);
|
||||||
qemu_co_mutex_init(&s->lock);
|
qemu_co_mutex_init(&s->lock);
|
||||||
QLIST_INIT(&s->tasks);
|
QLIST_INIT(&s->reqs);
|
||||||
QLIST_INIT(&s->calls);
|
QLIST_INIT(&s->calls);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
@ -470,7 +440,7 @@ static coroutine_fn int block_copy_task_run(AioTaskPool *pool,
|
|||||||
|
|
||||||
aio_task_pool_wait_slot(pool);
|
aio_task_pool_wait_slot(pool);
|
||||||
if (aio_task_pool_status(pool) < 0) {
|
if (aio_task_pool_status(pool) < 0) {
|
||||||
co_put_to_shres(task->s->mem, task->bytes);
|
co_put_to_shres(task->s->mem, task->req.bytes);
|
||||||
block_copy_task_end(task, -ECANCELED);
|
block_copy_task_end(task, -ECANCELED);
|
||||||
g_free(task);
|
g_free(task);
|
||||||
return -ECANCELED;
|
return -ECANCELED;
|
||||||
@ -583,7 +553,8 @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
|
|||||||
BlockCopyMethod method = t->method;
|
BlockCopyMethod method = t->method;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = block_copy_do_copy(s, t->offset, t->bytes, &method, &error_is_read);
|
ret = block_copy_do_copy(s, t->req.offset, t->req.bytes, &method,
|
||||||
|
&error_is_read);
|
||||||
|
|
||||||
WITH_QEMU_LOCK_GUARD(&s->lock) {
|
WITH_QEMU_LOCK_GUARD(&s->lock) {
|
||||||
if (s->method == t->method) {
|
if (s->method == t->method) {
|
||||||
@ -596,10 +567,10 @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
|
|||||||
t->call_state->error_is_read = error_is_read;
|
t->call_state->error_is_read = error_is_read;
|
||||||
}
|
}
|
||||||
} else if (s->progress) {
|
} else if (s->progress) {
|
||||||
progress_work_done(s->progress, t->bytes);
|
progress_work_done(s->progress, t->req.bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
co_put_to_shres(s->mem, t->bytes);
|
co_put_to_shres(s->mem, t->req.bytes);
|
||||||
block_copy_task_end(t, ret);
|
block_copy_task_end(t, ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -679,6 +650,18 @@ static int block_copy_is_cluster_allocated(BlockCopyState *s, int64_t offset,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void block_copy_reset(BlockCopyState *s, int64_t offset, int64_t bytes)
|
||||||
|
{
|
||||||
|
QEMU_LOCK_GUARD(&s->lock);
|
||||||
|
|
||||||
|
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
|
||||||
|
if (s->progress) {
|
||||||
|
progress_set_remaining(s->progress,
|
||||||
|
bdrv_get_dirty_count(s->copy_bitmap) +
|
||||||
|
s->in_flight_bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset bits in copy_bitmap starting at offset if they represent unallocated
|
* Reset bits in copy_bitmap starting at offset if they represent unallocated
|
||||||
* data in the image. May reset subsequent contiguous bits.
|
* data in the image. May reset subsequent contiguous bits.
|
||||||
@ -699,14 +682,7 @@ int64_t block_copy_reset_unallocated(BlockCopyState *s,
|
|||||||
bytes = clusters * s->cluster_size;
|
bytes = clusters * s->cluster_size;
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
qemu_co_mutex_lock(&s->lock);
|
block_copy_reset(s, offset, bytes);
|
||||||
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
|
|
||||||
if (s->progress) {
|
|
||||||
progress_set_remaining(s->progress,
|
|
||||||
bdrv_get_dirty_count(s->copy_bitmap) +
|
|
||||||
s->in_flight_bytes);
|
|
||||||
}
|
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*count = bytes;
|
*count = bytes;
|
||||||
@ -753,22 +729,22 @@ block_copy_dirty_clusters(BlockCopyCallState *call_state)
|
|||||||
trace_block_copy_skip_range(s, offset, bytes);
|
trace_block_copy_skip_range(s, offset, bytes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (task->offset > offset) {
|
if (task->req.offset > offset) {
|
||||||
trace_block_copy_skip_range(s, offset, task->offset - offset);
|
trace_block_copy_skip_range(s, offset, task->req.offset - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
found_dirty = true;
|
found_dirty = true;
|
||||||
|
|
||||||
ret = block_copy_block_status(s, task->offset, task->bytes,
|
ret = block_copy_block_status(s, task->req.offset, task->req.bytes,
|
||||||
&status_bytes);
|
&status_bytes);
|
||||||
assert(ret >= 0); /* never fail */
|
assert(ret >= 0); /* never fail */
|
||||||
if (status_bytes < task->bytes) {
|
if (status_bytes < task->req.bytes) {
|
||||||
block_copy_task_shrink(task, status_bytes);
|
block_copy_task_shrink(task, status_bytes);
|
||||||
}
|
}
|
||||||
if (qatomic_read(&s->skip_unallocated) &&
|
if (qatomic_read(&s->skip_unallocated) &&
|
||||||
!(ret & BDRV_BLOCK_ALLOCATED)) {
|
!(ret & BDRV_BLOCK_ALLOCATED)) {
|
||||||
block_copy_task_end(task, 0);
|
block_copy_task_end(task, 0);
|
||||||
trace_block_copy_skip_range(s, task->offset, task->bytes);
|
trace_block_copy_skip_range(s, task->req.offset, task->req.bytes);
|
||||||
offset = task_end(task);
|
offset = task_end(task);
|
||||||
bytes = end - offset;
|
bytes = end - offset;
|
||||||
g_free(task);
|
g_free(task);
|
||||||
@ -789,11 +765,11 @@ block_copy_dirty_clusters(BlockCopyCallState *call_state)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ratelimit_calculate_delay(&s->rate_limit, task->bytes);
|
ratelimit_calculate_delay(&s->rate_limit, task->req.bytes);
|
||||||
|
|
||||||
trace_block_copy_process(s, task->offset);
|
trace_block_copy_process(s, task->req.offset);
|
||||||
|
|
||||||
co_get_from_shres(s->mem, task->bytes);
|
co_get_from_shres(s->mem, task->req.bytes);
|
||||||
|
|
||||||
offset = task_end(task);
|
offset = task_end(task);
|
||||||
bytes = end - offset;
|
bytes = end - offset;
|
||||||
@ -861,8 +837,8 @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
|
|||||||
* Check that there is no task we still need to
|
* Check that there is no task we still need to
|
||||||
* wait to complete
|
* wait to complete
|
||||||
*/
|
*/
|
||||||
ret = block_copy_wait_one(s, call_state->offset,
|
ret = reqlist_wait_one(&s->reqs, call_state->offset,
|
||||||
call_state->bytes);
|
call_state->bytes, &s->lock);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
/*
|
/*
|
||||||
* No pending tasks, but check again the bitmap in this
|
* No pending tasks, but check again the bitmap in this
|
||||||
@ -870,7 +846,7 @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
|
|||||||
* between this and the critical section in
|
* between this and the critical section in
|
||||||
* block_copy_dirty_clusters().
|
* block_copy_dirty_clusters().
|
||||||
*
|
*
|
||||||
* block_copy_wait_one return value 0 also means that it
|
* reqlist_wait_one return value 0 also means that it
|
||||||
* didn't release the lock. So, we are still in the same
|
* didn't release the lock. So, we are still in the same
|
||||||
* critical section, not interrupted by any concurrent
|
* critical section, not interrupted by any concurrent
|
||||||
* access to state.
|
* access to state.
|
||||||
|
@ -33,10 +33,37 @@
|
|||||||
#include "block/block-copy.h"
|
#include "block/block-copy.h"
|
||||||
|
|
||||||
#include "block/copy-before-write.h"
|
#include "block/copy-before-write.h"
|
||||||
|
#include "block/reqlist.h"
|
||||||
|
|
||||||
|
#include "qapi/qapi-visit-block-core.h"
|
||||||
|
|
||||||
typedef struct BDRVCopyBeforeWriteState {
|
typedef struct BDRVCopyBeforeWriteState {
|
||||||
BlockCopyState *bcs;
|
BlockCopyState *bcs;
|
||||||
BdrvChild *target;
|
BdrvChild *target;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @lock: protects access to @access_bitmap, @done_bitmap and
|
||||||
|
* @frozen_read_reqs
|
||||||
|
*/
|
||||||
|
CoMutex lock;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @access_bitmap: represents areas allowed for reading by fleecing user.
|
||||||
|
* Reading from non-dirty areas leads to -EACCES.
|
||||||
|
*/
|
||||||
|
BdrvDirtyBitmap *access_bitmap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @done_bitmap: represents areas that was successfully copied to @target by
|
||||||
|
* copy-before-write operations.
|
||||||
|
*/
|
||||||
|
BdrvDirtyBitmap *done_bitmap;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @frozen_read_reqs: current read requests for fleecing user in bs->file
|
||||||
|
* node. These areas must not be rewritten by guest.
|
||||||
|
*/
|
||||||
|
BlockReqList frozen_read_reqs;
|
||||||
} BDRVCopyBeforeWriteState;
|
} BDRVCopyBeforeWriteState;
|
||||||
|
|
||||||
static coroutine_fn int cbw_co_preadv(
|
static coroutine_fn int cbw_co_preadv(
|
||||||
@ -46,10 +73,20 @@ static coroutine_fn int cbw_co_preadv(
|
|||||||
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do copy-before-write operation.
|
||||||
|
*
|
||||||
|
* On failure guest request must be failed too.
|
||||||
|
*
|
||||||
|
* On success, we also wait for all in-flight fleecing read requests in source
|
||||||
|
* node, and it's guaranteed that after cbw_do_copy_before_write() successful
|
||||||
|
* return there are no such requests and they will never appear.
|
||||||
|
*/
|
||||||
static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs,
|
static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs,
|
||||||
uint64_t offset, uint64_t bytes, BdrvRequestFlags flags)
|
uint64_t offset, uint64_t bytes, BdrvRequestFlags flags)
|
||||||
{
|
{
|
||||||
BDRVCopyBeforeWriteState *s = bs->opaque;
|
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||||
|
int ret;
|
||||||
uint64_t off, end;
|
uint64_t off, end;
|
||||||
int64_t cluster_size = block_copy_cluster_size(s->bcs);
|
int64_t cluster_size = block_copy_cluster_size(s->bcs);
|
||||||
|
|
||||||
@ -60,7 +97,17 @@ static coroutine_fn int cbw_do_copy_before_write(BlockDriverState *bs,
|
|||||||
off = QEMU_ALIGN_DOWN(offset, cluster_size);
|
off = QEMU_ALIGN_DOWN(offset, cluster_size);
|
||||||
end = QEMU_ALIGN_UP(offset + bytes, cluster_size);
|
end = QEMU_ALIGN_UP(offset + bytes, cluster_size);
|
||||||
|
|
||||||
return block_copy(s->bcs, off, end - off, true);
|
ret = block_copy(s->bcs, off, end - off, true);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
WITH_QEMU_LOCK_GUARD(&s->lock) {
|
||||||
|
bdrv_set_dirty_bitmap(s->done_bitmap, off, end - off);
|
||||||
|
reqlist_wait_all(&s->frozen_read_reqs, off, end - off, &s->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs,
|
static int coroutine_fn cbw_co_pdiscard(BlockDriverState *bs,
|
||||||
@ -108,6 +155,142 @@ static int coroutine_fn cbw_co_flush(BlockDriverState *bs)
|
|||||||
return bdrv_co_flush(bs->file->bs);
|
return bdrv_co_flush(bs->file->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If @offset not accessible - return NULL.
|
||||||
|
*
|
||||||
|
* Otherwise, set @pnum to some bytes that accessible from @file (@file is set
|
||||||
|
* to bs->file or to s->target). Return newly allocated BlockReq object that
|
||||||
|
* should be than passed to cbw_snapshot_read_unlock().
|
||||||
|
*
|
||||||
|
* It's guaranteed that guest writes will not interact in the region until
|
||||||
|
* cbw_snapshot_read_unlock() called.
|
||||||
|
*/
|
||||||
|
static BlockReq *cbw_snapshot_read_lock(BlockDriverState *bs,
|
||||||
|
int64_t offset, int64_t bytes,
|
||||||
|
int64_t *pnum, BdrvChild **file)
|
||||||
|
{
|
||||||
|
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||||
|
BlockReq *req = g_new(BlockReq, 1);
|
||||||
|
bool done;
|
||||||
|
|
||||||
|
QEMU_LOCK_GUARD(&s->lock);
|
||||||
|
|
||||||
|
if (bdrv_dirty_bitmap_next_zero(s->access_bitmap, offset, bytes) != -1) {
|
||||||
|
g_free(req);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
done = bdrv_dirty_bitmap_status(s->done_bitmap, offset, bytes, pnum);
|
||||||
|
if (done) {
|
||||||
|
/*
|
||||||
|
* Special invalid BlockReq, that is handled in
|
||||||
|
* cbw_snapshot_read_unlock(). We don't need to lock something to read
|
||||||
|
* from s->target.
|
||||||
|
*/
|
||||||
|
*req = (BlockReq) {.offset = -1, .bytes = -1};
|
||||||
|
*file = s->target;
|
||||||
|
} else {
|
||||||
|
reqlist_init_req(&s->frozen_read_reqs, req, offset, bytes);
|
||||||
|
*file = bs->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cbw_snapshot_read_unlock(BlockDriverState *bs, BlockReq *req)
|
||||||
|
{
|
||||||
|
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||||
|
|
||||||
|
if (req->offset == -1 && req->bytes == -1) {
|
||||||
|
g_free(req);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QEMU_LOCK_GUARD(&s->lock);
|
||||||
|
|
||||||
|
reqlist_remove_req(req);
|
||||||
|
g_free(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
static coroutine_fn int
|
||||||
|
cbw_co_preadv_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes,
|
||||||
|
QEMUIOVector *qiov, size_t qiov_offset)
|
||||||
|
{
|
||||||
|
BlockReq *req;
|
||||||
|
BdrvChild *file;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* TODO: upgrade to async loop using AioTask */
|
||||||
|
while (bytes) {
|
||||||
|
int64_t cur_bytes;
|
||||||
|
|
||||||
|
req = cbw_snapshot_read_lock(bs, offset, bytes, &cur_bytes, &file);
|
||||||
|
if (!req) {
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bdrv_co_preadv_part(file, offset, cur_bytes,
|
||||||
|
qiov, qiov_offset, 0);
|
||||||
|
cbw_snapshot_read_unlock(bs, req);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes -= cur_bytes;
|
||||||
|
offset += cur_bytes;
|
||||||
|
qiov_offset += cur_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn
|
||||||
|
cbw_co_snapshot_block_status(BlockDriverState *bs,
|
||||||
|
bool want_zero, int64_t offset, int64_t bytes,
|
||||||
|
int64_t *pnum, int64_t *map,
|
||||||
|
BlockDriverState **file)
|
||||||
|
{
|
||||||
|
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||||
|
BlockReq *req;
|
||||||
|
int ret;
|
||||||
|
int64_t cur_bytes;
|
||||||
|
BdrvChild *child;
|
||||||
|
|
||||||
|
req = cbw_snapshot_read_lock(bs, offset, bytes, &cur_bytes, &child);
|
||||||
|
if (!req) {
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bdrv_block_status(child->bs, offset, cur_bytes, pnum, map, file);
|
||||||
|
if (child == s->target) {
|
||||||
|
/*
|
||||||
|
* We refer to s->target only for areas that we've written to it.
|
||||||
|
* And we can not report unallocated blocks in s->target: this will
|
||||||
|
* break generic block-status-above logic, that will go to
|
||||||
|
* copy-before-write filtered child in this case.
|
||||||
|
*/
|
||||||
|
assert(ret & BDRV_BLOCK_ALLOCATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
cbw_snapshot_read_unlock(bs, req);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn cbw_co_pdiscard_snapshot(BlockDriverState *bs,
|
||||||
|
int64_t offset, int64_t bytes)
|
||||||
|
{
|
||||||
|
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||||
|
|
||||||
|
WITH_QEMU_LOCK_GUARD(&s->lock) {
|
||||||
|
bdrv_reset_dirty_bitmap(s->access_bitmap, offset, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
block_copy_reset(s->bcs, offset, bytes);
|
||||||
|
|
||||||
|
return bdrv_co_pdiscard(s->target, offset, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
static void cbw_refresh_filename(BlockDriverState *bs)
|
static void cbw_refresh_filename(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
|
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
|
||||||
@ -145,11 +328,54 @@ static void cbw_child_perm(BlockDriverState *bs, BdrvChild *c,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool cbw_parse_bitmap_option(QDict *options, BdrvDirtyBitmap **bitmap,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
QDict *bitmap_qdict = NULL;
|
||||||
|
BlockDirtyBitmap *bmp_param = NULL;
|
||||||
|
Visitor *v = NULL;
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
*bitmap = NULL;
|
||||||
|
|
||||||
|
qdict_extract_subqdict(options, &bitmap_qdict, "bitmap.");
|
||||||
|
if (!qdict_size(bitmap_qdict)) {
|
||||||
|
ret = true;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = qobject_input_visitor_new_flat_confused(bitmap_qdict, errp);
|
||||||
|
if (!v) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
visit_type_BlockDirtyBitmap(v, NULL, &bmp_param, errp);
|
||||||
|
if (!bmp_param) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
*bitmap = block_dirty_bitmap_lookup(bmp_param->node, bmp_param->name, NULL,
|
||||||
|
errp);
|
||||||
|
if (!*bitmap) {
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
out:
|
||||||
|
qapi_free_BlockDirtyBitmap(bmp_param);
|
||||||
|
visit_free(v);
|
||||||
|
qobject_unref(bitmap_qdict);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int cbw_open(BlockDriverState *bs, QDict *options, int flags,
|
static int cbw_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
BDRVCopyBeforeWriteState *s = bs->opaque;
|
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||||
BdrvDirtyBitmap *copy_bitmap;
|
BdrvDirtyBitmap *bitmap = NULL;
|
||||||
|
int64_t cluster_size;
|
||||||
|
|
||||||
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
|
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
|
||||||
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
|
BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
|
||||||
@ -164,6 +390,10 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!cbw_parse_bitmap_option(options, &bitmap, errp)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
bs->total_sectors = bs->file->bs->total_sectors;
|
bs->total_sectors = bs->file->bs->total_sectors;
|
||||||
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
|
||||||
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
|
(BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
|
||||||
@ -171,14 +401,32 @@ static int cbw_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
|
||||||
bs->file->bs->supported_zero_flags);
|
bs->file->bs->supported_zero_flags);
|
||||||
|
|
||||||
s->bcs = block_copy_state_new(bs->file, s->target, errp);
|
s->bcs = block_copy_state_new(bs->file, s->target, bitmap, errp);
|
||||||
if (!s->bcs) {
|
if (!s->bcs) {
|
||||||
error_prepend(errp, "Cannot create block-copy-state: ");
|
error_prepend(errp, "Cannot create block-copy-state: ");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
copy_bitmap = block_copy_dirty_bitmap(s->bcs);
|
cluster_size = block_copy_cluster_size(s->bcs);
|
||||||
bdrv_set_dirty_bitmap(copy_bitmap, 0, bdrv_dirty_bitmap_size(copy_bitmap));
|
|
||||||
|
s->done_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp);
|
||||||
|
if (!s->done_bitmap) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
bdrv_disable_dirty_bitmap(s->done_bitmap);
|
||||||
|
|
||||||
|
/* s->access_bitmap starts equal to bcs bitmap */
|
||||||
|
s->access_bitmap = bdrv_create_dirty_bitmap(bs, cluster_size, NULL, errp);
|
||||||
|
if (!s->access_bitmap) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
bdrv_disable_dirty_bitmap(s->access_bitmap);
|
||||||
|
bdrv_dirty_bitmap_merge_internal(s->access_bitmap,
|
||||||
|
block_copy_dirty_bitmap(s->bcs), NULL,
|
||||||
|
true);
|
||||||
|
|
||||||
|
qemu_co_mutex_init(&s->lock);
|
||||||
|
QLIST_INIT(&s->frozen_read_reqs);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -187,6 +435,9 @@ static void cbw_close(BlockDriverState *bs)
|
|||||||
{
|
{
|
||||||
BDRVCopyBeforeWriteState *s = bs->opaque;
|
BDRVCopyBeforeWriteState *s = bs->opaque;
|
||||||
|
|
||||||
|
bdrv_release_dirty_bitmap(s->access_bitmap);
|
||||||
|
bdrv_release_dirty_bitmap(s->done_bitmap);
|
||||||
|
|
||||||
block_copy_state_free(s->bcs);
|
block_copy_state_free(s->bcs);
|
||||||
s->bcs = NULL;
|
s->bcs = NULL;
|
||||||
}
|
}
|
||||||
@ -204,6 +455,10 @@ BlockDriver bdrv_cbw_filter = {
|
|||||||
.bdrv_co_pdiscard = cbw_co_pdiscard,
|
.bdrv_co_pdiscard = cbw_co_pdiscard,
|
||||||
.bdrv_co_flush = cbw_co_flush,
|
.bdrv_co_flush = cbw_co_flush,
|
||||||
|
|
||||||
|
.bdrv_co_preadv_snapshot = cbw_co_preadv_snapshot,
|
||||||
|
.bdrv_co_pdiscard_snapshot = cbw_co_pdiscard_snapshot,
|
||||||
|
.bdrv_co_snapshot_block_status = cbw_co_snapshot_block_status,
|
||||||
|
|
||||||
.bdrv_refresh_filename = cbw_refresh_filename,
|
.bdrv_refresh_filename = cbw_refresh_filename,
|
||||||
|
|
||||||
.bdrv_child_perm = cbw_child_perm,
|
.bdrv_child_perm = cbw_child_perm,
|
||||||
|
94
block/curl.c
94
block/curl.c
@ -458,38 +458,51 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
|
|||||||
if (!state->curl) {
|
if (!state->curl) {
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
|
if (curl_easy_setopt(state->curl, CURLOPT_URL, s->url) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
|
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
|
||||||
(long) s->sslverify);
|
(long) s->sslverify) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST,
|
curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST,
|
||||||
s->sslverify ? 2L : 0L);
|
s->sslverify ? 2L : 0L)) {
|
||||||
if (s->cookie) {
|
goto err;
|
||||||
curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie);
|
}
|
||||||
|
if (s->cookie) {
|
||||||
|
if (curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout) ||
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
|
||||||
|
(void *)curl_read_cb) ||
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) ||
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state) ||
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1) ||
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1) ||
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1) ||
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg) ||
|
||||||
|
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1)) {
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout);
|
|
||||||
curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
|
|
||||||
(void *)curl_read_cb);
|
|
||||||
curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state);
|
|
||||||
curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state);
|
|
||||||
curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1);
|
|
||||||
curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1);
|
|
||||||
curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1);
|
|
||||||
curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
|
|
||||||
curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
|
|
||||||
|
|
||||||
if (s->username) {
|
if (s->username) {
|
||||||
curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username);
|
if (curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (s->password) {
|
if (s->password) {
|
||||||
curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password);
|
if (curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (s->proxyusername) {
|
if (s->proxyusername) {
|
||||||
curl_easy_setopt(state->curl,
|
if (curl_easy_setopt(state->curl,
|
||||||
CURLOPT_PROXYUSERNAME, s->proxyusername);
|
CURLOPT_PROXYUSERNAME, s->proxyusername)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (s->proxypassword) {
|
if (s->proxypassword) {
|
||||||
curl_easy_setopt(state->curl,
|
if (curl_easy_setopt(state->curl,
|
||||||
CURLOPT_PROXYPASSWORD, s->proxypassword);
|
CURLOPT_PROXYPASSWORD, s->proxypassword)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Restrict supported protocols to avoid security issues in the more
|
/* Restrict supported protocols to avoid security issues in the more
|
||||||
@ -499,18 +512,27 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state)
|
|||||||
* Restricting protocols is only supported from 7.19.4 upwards.
|
* Restricting protocols is only supported from 7.19.4 upwards.
|
||||||
*/
|
*/
|
||||||
#if LIBCURL_VERSION_NUM >= 0x071304
|
#if LIBCURL_VERSION_NUM >= 0x071304
|
||||||
curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS);
|
if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS);
|
curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DEBUG_VERBOSE
|
#ifdef DEBUG_VERBOSE
|
||||||
curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);
|
if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
state->s = s;
|
state->s = s;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
curl_easy_cleanup(state->curl);
|
||||||
|
state->curl = NULL;
|
||||||
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Called with s->mutex held. */
|
/* Called with s->mutex held. */
|
||||||
@ -759,14 +781,19 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
|
|||||||
// Get file size
|
// Get file size
|
||||||
|
|
||||||
if (curl_init_state(s, state) < 0) {
|
if (curl_init_state(s, state) < 0) {
|
||||||
|
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
|
||||||
|
"curl library initialization failed.");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->accept_range = false;
|
s->accept_range = false;
|
||||||
curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
|
if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1) ||
|
||||||
curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION,
|
curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, curl_header_cb) ||
|
||||||
curl_header_cb);
|
curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s)) {
|
||||||
curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s);
|
pstrcpy(state->errmsg, CURL_ERROR_SIZE,
|
||||||
|
"curl library initialization failed.");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
if (curl_easy_perform(state->curl))
|
if (curl_easy_perform(state->curl))
|
||||||
goto out;
|
goto out;
|
||||||
if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d)) {
|
if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d)) {
|
||||||
@ -879,9 +906,8 @@ static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
|
|||||||
|
|
||||||
snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end);
|
snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end);
|
||||||
trace_curl_setup_preadv(acb->bytes, start, state->range);
|
trace_curl_setup_preadv(acb->bytes, start, state->range);
|
||||||
curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
|
if (curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range) ||
|
||||||
|
curl_multi_add_handle(s->multi, state->curl) != CURLM_OK) {
|
||||||
if (curl_multi_add_handle(s->multi, state->curl) != CURLM_OK) {
|
|
||||||
state->acb[0] = NULL;
|
state->acb[0] = NULL;
|
||||||
acb->ret = -EIO;
|
acb->ret = -EIO;
|
||||||
|
|
||||||
|
@ -879,16 +879,25 @@ bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
|
|||||||
dirty_start, dirty_count);
|
dirty_start, dirty_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap, int64_t offset,
|
||||||
|
int64_t bytes, int64_t *count)
|
||||||
|
{
|
||||||
|
return hbitmap_status(bitmap->bitmap, offset, bytes, count);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bdrv_merge_dirty_bitmap: merge src into dest.
|
* bdrv_merge_dirty_bitmap: merge src into dest.
|
||||||
* Ensures permissions on bitmaps are reasonable; use for public API.
|
* Ensures permissions on bitmaps are reasonable; use for public API.
|
||||||
*
|
*
|
||||||
* @backup: If provided, make a copy of dest here prior to merge.
|
* @backup: If provided, make a copy of dest here prior to merge.
|
||||||
|
*
|
||||||
|
* Returns true on success, false on failure. In case of failure bitmaps are
|
||||||
|
* untouched.
|
||||||
*/
|
*/
|
||||||
void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
||||||
HBitmap **backup, Error **errp)
|
HBitmap **backup, Error **errp)
|
||||||
{
|
{
|
||||||
bool ret;
|
bool ret = false;
|
||||||
|
|
||||||
bdrv_dirty_bitmaps_lock(dest->bs);
|
bdrv_dirty_bitmaps_lock(dest->bs);
|
||||||
if (src->bs != dest->bs) {
|
if (src->bs != dest->bs) {
|
||||||
@ -916,6 +925,8 @@ out:
|
|||||||
if (src->bs != dest->bs) {
|
if (src->bs != dest->bs) {
|
||||||
bdrv_dirty_bitmaps_unlock(src->bs);
|
bdrv_dirty_bitmaps_unlock(src->bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
76
block/io.c
76
block/io.c
@ -2203,6 +2203,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
|
|||||||
|
|
||||||
padding = bdrv_init_padding(bs, offset, bytes, &pad);
|
padding = bdrv_init_padding(bs, offset, bytes, &pad);
|
||||||
if (padding) {
|
if (padding) {
|
||||||
|
assert(!(flags & BDRV_REQ_NO_WAIT));
|
||||||
bdrv_make_request_serialising(req, align);
|
bdrv_make_request_serialising(req, align);
|
||||||
|
|
||||||
bdrv_padding_rmw_read(child, req, &pad, true);
|
bdrv_padding_rmw_read(child, req, &pad, true);
|
||||||
@ -2339,6 +2340,7 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child,
|
|||||||
* serialize the request to prevent interactions of the
|
* serialize the request to prevent interactions of the
|
||||||
* widened region with other transactions.
|
* widened region with other transactions.
|
||||||
*/
|
*/
|
||||||
|
assert(!(flags & BDRV_REQ_NO_WAIT));
|
||||||
bdrv_make_request_serialising(&req, align);
|
bdrv_make_request_serialising(&req, align);
|
||||||
bdrv_padding_rmw_read(child, &req, &pad, false);
|
bdrv_padding_rmw_read(child, &req, &pad, false);
|
||||||
}
|
}
|
||||||
@ -3387,6 +3389,8 @@ static int coroutine_fn bdrv_co_copy_range_internal(
|
|||||||
/* TODO We can support BDRV_REQ_NO_FALLBACK here */
|
/* TODO We can support BDRV_REQ_NO_FALLBACK here */
|
||||||
assert(!(read_flags & BDRV_REQ_NO_FALLBACK));
|
assert(!(read_flags & BDRV_REQ_NO_FALLBACK));
|
||||||
assert(!(write_flags & BDRV_REQ_NO_FALLBACK));
|
assert(!(write_flags & BDRV_REQ_NO_FALLBACK));
|
||||||
|
assert(!(read_flags & BDRV_REQ_NO_WAIT));
|
||||||
|
assert(!(write_flags & BDRV_REQ_NO_WAIT));
|
||||||
|
|
||||||
if (!dst || !dst->bs || !bdrv_is_inserted(dst->bs)) {
|
if (!dst || !dst->bs || !bdrv_is_inserted(dst->bs)) {
|
||||||
return -ENOMEDIUM;
|
return -ENOMEDIUM;
|
||||||
@ -3650,3 +3654,75 @@ void bdrv_cancel_in_flight(BlockDriverState *bs)
|
|||||||
bs->drv->bdrv_cancel_in_flight(bs);
|
bs->drv->bdrv_cancel_in_flight(bs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int coroutine_fn
|
||||||
|
bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes,
|
||||||
|
QEMUIOVector *qiov, size_t qiov_offset)
|
||||||
|
{
|
||||||
|
BlockDriverState *bs = child->bs;
|
||||||
|
BlockDriver *drv = bs->drv;
|
||||||
|
int ret;
|
||||||
|
IO_CODE();
|
||||||
|
|
||||||
|
if (!drv) {
|
||||||
|
return -ENOMEDIUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drv->bdrv_co_preadv_snapshot) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdrv_inc_in_flight(bs);
|
||||||
|
ret = drv->bdrv_co_preadv_snapshot(bs, offset, bytes, qiov, qiov_offset);
|
||||||
|
bdrv_dec_in_flight(bs);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int coroutine_fn
|
||||||
|
bdrv_co_snapshot_block_status(BlockDriverState *bs,
|
||||||
|
bool want_zero, int64_t offset, int64_t bytes,
|
||||||
|
int64_t *pnum, int64_t *map,
|
||||||
|
BlockDriverState **file)
|
||||||
|
{
|
||||||
|
BlockDriver *drv = bs->drv;
|
||||||
|
int ret;
|
||||||
|
IO_CODE();
|
||||||
|
|
||||||
|
if (!drv) {
|
||||||
|
return -ENOMEDIUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drv->bdrv_co_snapshot_block_status) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdrv_inc_in_flight(bs);
|
||||||
|
ret = drv->bdrv_co_snapshot_block_status(bs, want_zero, offset, bytes,
|
||||||
|
pnum, map, file);
|
||||||
|
bdrv_dec_in_flight(bs);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int coroutine_fn
|
||||||
|
bdrv_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes)
|
||||||
|
{
|
||||||
|
BlockDriver *drv = bs->drv;
|
||||||
|
int ret;
|
||||||
|
IO_CODE();
|
||||||
|
|
||||||
|
if (!drv) {
|
||||||
|
return -ENOMEDIUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!drv->bdrv_co_pdiscard_snapshot) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
bdrv_inc_in_flight(bs);
|
||||||
|
ret = drv->bdrv_co_pdiscard_snapshot(bs, offset, bytes);
|
||||||
|
bdrv_dec_in_flight(bs);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -32,7 +32,9 @@ block_ss.add(files(
|
|||||||
'qcow2.c',
|
'qcow2.c',
|
||||||
'quorum.c',
|
'quorum.c',
|
||||||
'raw-format.c',
|
'raw-format.c',
|
||||||
|
'reqlist.c',
|
||||||
'snapshot.c',
|
'snapshot.c',
|
||||||
|
'snapshot-access.c',
|
||||||
'throttle-groups.c',
|
'throttle-groups.c',
|
||||||
'throttle.c',
|
'throttle.c',
|
||||||
'vhdx-endian.c',
|
'vhdx-endian.c',
|
||||||
|
@ -263,7 +263,6 @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target,
|
|||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
BdrvDirtyBitmap *dst, *src, *anon;
|
BdrvDirtyBitmap *dst, *src, *anon;
|
||||||
BlockDirtyBitmapMergeSourceList *lst;
|
BlockDirtyBitmapMergeSourceList *lst;
|
||||||
Error *local_err = NULL;
|
|
||||||
|
|
||||||
GLOBAL_STATE_CODE();
|
GLOBAL_STATE_CODE();
|
||||||
|
|
||||||
@ -303,9 +302,7 @@ BdrvDirtyBitmap *block_dirty_bitmap_merge(const char *node, const char *target,
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_merge_dirty_bitmap(anon, src, NULL, &local_err);
|
if (!bdrv_merge_dirty_bitmap(anon, src, NULL, errp)) {
|
||||||
if (local_err) {
|
|
||||||
error_propagate(errp, local_err);
|
|
||||||
dst = NULL;
|
dst = NULL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -276,6 +276,10 @@ static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset,
|
|||||||
int64_t end = offset + bytes;
|
int64_t end = offset + bytes;
|
||||||
int64_t prealloc_start, prealloc_end;
|
int64_t prealloc_start, prealloc_end;
|
||||||
int ret;
|
int ret;
|
||||||
|
uint32_t file_align = bs->file->bs->bl.request_alignment;
|
||||||
|
uint32_t prealloc_align = MAX(s->opts.prealloc_align, file_align);
|
||||||
|
|
||||||
|
assert(QEMU_IS_ALIGNED(prealloc_align, file_align));
|
||||||
|
|
||||||
if (!has_prealloc_perms(bs)) {
|
if (!has_prealloc_perms(bs)) {
|
||||||
/* We don't have state neither should try to recover it */
|
/* We don't have state neither should try to recover it */
|
||||||
@ -320,9 +324,14 @@ static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset,
|
|||||||
|
|
||||||
/* Now we want new preallocation, as request writes beyond s->file_end. */
|
/* Now we want new preallocation, as request writes beyond s->file_end. */
|
||||||
|
|
||||||
prealloc_start = want_merge_zero ? MIN(offset, s->file_end) : s->file_end;
|
prealloc_start = QEMU_ALIGN_UP(
|
||||||
prealloc_end = QEMU_ALIGN_UP(end + s->opts.prealloc_size,
|
want_merge_zero ? MIN(offset, s->file_end) : s->file_end,
|
||||||
s->opts.prealloc_align);
|
file_align);
|
||||||
|
prealloc_end = QEMU_ALIGN_UP(
|
||||||
|
MAX(prealloc_start, end) + s->opts.prealloc_size,
|
||||||
|
prealloc_align);
|
||||||
|
|
||||||
|
want_merge_zero = want_merge_zero && (prealloc_start <= offset);
|
||||||
|
|
||||||
ret = bdrv_co_pwrite_zeroes(
|
ret = bdrv_co_pwrite_zeroes(
|
||||||
bs->file, prealloc_start, prealloc_end - prealloc_start,
|
bs->file, prealloc_start, prealloc_end - prealloc_start,
|
||||||
|
85
block/reqlist.c
Normal file
85
block/reqlist.c
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* reqlist API
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Proxmox Server Solutions
|
||||||
|
* Copyright (c) 2021 Virtuozzo International GmbH.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Dietmar Maurer (dietmar@proxmox.com)
|
||||||
|
* Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "qemu/range.h"
|
||||||
|
|
||||||
|
#include "block/reqlist.h"
|
||||||
|
|
||||||
|
void reqlist_init_req(BlockReqList *reqs, BlockReq *req, int64_t offset,
|
||||||
|
int64_t bytes)
|
||||||
|
{
|
||||||
|
assert(!reqlist_find_conflict(reqs, offset, bytes));
|
||||||
|
|
||||||
|
*req = (BlockReq) {
|
||||||
|
.offset = offset,
|
||||||
|
.bytes = bytes,
|
||||||
|
};
|
||||||
|
qemu_co_queue_init(&req->wait_queue);
|
||||||
|
QLIST_INSERT_HEAD(reqs, req, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockReq *reqlist_find_conflict(BlockReqList *reqs, int64_t offset,
|
||||||
|
int64_t bytes)
|
||||||
|
{
|
||||||
|
BlockReq *r;
|
||||||
|
|
||||||
|
QLIST_FOREACH(r, reqs, list) {
|
||||||
|
if (ranges_overlap(offset, bytes, r->offset, r->bytes)) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool coroutine_fn reqlist_wait_one(BlockReqList *reqs, int64_t offset,
|
||||||
|
int64_t bytes, CoMutex *lock)
|
||||||
|
{
|
||||||
|
BlockReq *r = reqlist_find_conflict(reqs, offset, bytes);
|
||||||
|
|
||||||
|
if (!r) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_co_queue_wait(&r->wait_queue, lock);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void coroutine_fn reqlist_wait_all(BlockReqList *reqs, int64_t offset,
|
||||||
|
int64_t bytes, CoMutex *lock)
|
||||||
|
{
|
||||||
|
while (reqlist_wait_one(reqs, offset, bytes, lock)) {
|
||||||
|
/* continue */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void coroutine_fn reqlist_shrink_req(BlockReq *req, int64_t new_bytes)
|
||||||
|
{
|
||||||
|
if (new_bytes == req->bytes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(new_bytes > 0 && new_bytes < req->bytes);
|
||||||
|
|
||||||
|
req->bytes = new_bytes;
|
||||||
|
qemu_co_queue_restart_all(&req->wait_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void coroutine_fn reqlist_remove_req(BlockReq *req)
|
||||||
|
{
|
||||||
|
QLIST_REMOVE(req, list);
|
||||||
|
qemu_co_queue_restart_all(&req->wait_queue);
|
||||||
|
}
|
132
block/snapshot-access.c
Normal file
132
block/snapshot-access.c
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* snapshot_access block driver
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Virtuozzo International GmbH.
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "sysemu/block-backend.h"
|
||||||
|
#include "qemu/cutils.h"
|
||||||
|
#include "block/block_int.h"
|
||||||
|
|
||||||
|
static coroutine_fn int
|
||||||
|
snapshot_access_co_preadv_part(BlockDriverState *bs,
|
||||||
|
int64_t offset, int64_t bytes,
|
||||||
|
QEMUIOVector *qiov, size_t qiov_offset,
|
||||||
|
BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
if (flags) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bdrv_co_preadv_snapshot(bs->file, offset, bytes, qiov, qiov_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn
|
||||||
|
snapshot_access_co_block_status(BlockDriverState *bs,
|
||||||
|
bool want_zero, int64_t offset,
|
||||||
|
int64_t bytes, int64_t *pnum,
|
||||||
|
int64_t *map, BlockDriverState **file)
|
||||||
|
{
|
||||||
|
return bdrv_co_snapshot_block_status(bs->file->bs, want_zero, offset,
|
||||||
|
bytes, pnum, map, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn snapshot_access_co_pdiscard(BlockDriverState *bs,
|
||||||
|
int64_t offset, int64_t bytes)
|
||||||
|
{
|
||||||
|
return bdrv_co_pdiscard_snapshot(bs->file->bs, offset, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int coroutine_fn
|
||||||
|
snapshot_access_co_pwrite_zeroes(BlockDriverState *bs,
|
||||||
|
int64_t offset, int64_t bytes,
|
||||||
|
BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static coroutine_fn int
|
||||||
|
snapshot_access_co_pwritev_part(BlockDriverState *bs,
|
||||||
|
int64_t offset, int64_t bytes,
|
||||||
|
QEMUIOVector *qiov, size_t qiov_offset,
|
||||||
|
BdrvRequestFlags flags)
|
||||||
|
{
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void snapshot_access_refresh_filename(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
|
||||||
|
bs->file->bs->filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snapshot_access_open(BlockDriverState *bs, QDict *options, int flags,
|
||||||
|
Error **errp)
|
||||||
|
{
|
||||||
|
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
|
||||||
|
BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY,
|
||||||
|
false, errp);
|
||||||
|
if (!bs->file) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bs->total_sectors = bs->file->bs->total_sectors;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void snapshot_access_child_perm(BlockDriverState *bs, BdrvChild *c,
|
||||||
|
BdrvChildRole role,
|
||||||
|
BlockReopenQueue *reopen_queue,
|
||||||
|
uint64_t perm, uint64_t shared,
|
||||||
|
uint64_t *nperm, uint64_t *nshared)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Currently, we don't need any permissions. If bs->file provides
|
||||||
|
* snapshot-access API, we can use it.
|
||||||
|
*/
|
||||||
|
*nperm = 0;
|
||||||
|
*nshared = BLK_PERM_ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockDriver bdrv_snapshot_access_drv = {
|
||||||
|
.format_name = "snapshot-access",
|
||||||
|
|
||||||
|
.bdrv_open = snapshot_access_open,
|
||||||
|
|
||||||
|
.bdrv_co_preadv_part = snapshot_access_co_preadv_part,
|
||||||
|
.bdrv_co_pwritev_part = snapshot_access_co_pwritev_part,
|
||||||
|
.bdrv_co_pwrite_zeroes = snapshot_access_co_pwrite_zeroes,
|
||||||
|
.bdrv_co_pdiscard = snapshot_access_co_pdiscard,
|
||||||
|
.bdrv_co_block_status = snapshot_access_co_block_status,
|
||||||
|
|
||||||
|
.bdrv_refresh_filename = snapshot_access_refresh_filename,
|
||||||
|
|
||||||
|
.bdrv_child_perm = snapshot_access_child_perm,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void snapshot_access_init(void)
|
||||||
|
{
|
||||||
|
bdrv_register(&bdrv_snapshot_access_drv);
|
||||||
|
}
|
||||||
|
|
||||||
|
block_init(snapshot_access_init);
|
@ -434,12 +434,16 @@ static const AIOCBInfo trim_aiocb_info = {
|
|||||||
static void ide_trim_bh_cb(void *opaque)
|
static void ide_trim_bh_cb(void *opaque)
|
||||||
{
|
{
|
||||||
TrimAIOCB *iocb = opaque;
|
TrimAIOCB *iocb = opaque;
|
||||||
|
BlockBackend *blk = iocb->s->blk;
|
||||||
|
|
||||||
iocb->common.cb(iocb->common.opaque, iocb->ret);
|
iocb->common.cb(iocb->common.opaque, iocb->ret);
|
||||||
|
|
||||||
qemu_bh_delete(iocb->bh);
|
qemu_bh_delete(iocb->bh);
|
||||||
iocb->bh = NULL;
|
iocb->bh = NULL;
|
||||||
qemu_aio_unref(iocb);
|
qemu_aio_unref(iocb);
|
||||||
|
|
||||||
|
/* Paired with an increment in ide_issue_trim() */
|
||||||
|
blk_dec_in_flight(blk);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ide_issue_trim_cb(void *opaque, int ret)
|
static void ide_issue_trim_cb(void *opaque, int ret)
|
||||||
@ -509,6 +513,9 @@ BlockAIOCB *ide_issue_trim(
|
|||||||
IDEState *s = opaque;
|
IDEState *s = opaque;
|
||||||
TrimAIOCB *iocb;
|
TrimAIOCB *iocb;
|
||||||
|
|
||||||
|
/* Paired with a decrement in ide_trim_bh_cb() */
|
||||||
|
blk_inc_in_flight(s->blk);
|
||||||
|
|
||||||
iocb = blk_aio_get(&trim_aiocb_info, s->blk, cb, cb_opaque);
|
iocb = blk_aio_get(&trim_aiocb_info, s->blk, cb, cb_opaque);
|
||||||
iocb->s = s;
|
iocb->s = s;
|
||||||
iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb);
|
iocb->bh = qemu_bh_new(ide_trim_bh_cb, iocb);
|
||||||
|
@ -112,7 +112,8 @@ typedef enum {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* If we need to wait for other requests, just fail immediately. Used
|
* If we need to wait for other requests, just fail immediately. Used
|
||||||
* only together with BDRV_REQ_SERIALISING.
|
* only together with BDRV_REQ_SERIALISING. Used only with requests aligned
|
||||||
|
* to request_alignment (corresponding assertions are in block/io.c).
|
||||||
*/
|
*/
|
||||||
BDRV_REQ_NO_WAIT = 0x400,
|
BDRV_REQ_NO_WAIT = 0x400,
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ typedef struct BlockCopyState BlockCopyState;
|
|||||||
typedef struct BlockCopyCallState BlockCopyCallState;
|
typedef struct BlockCopyCallState BlockCopyCallState;
|
||||||
|
|
||||||
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||||
|
const BdrvDirtyBitmap *bitmap,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
/* Function should be called prior any actual copy request */
|
/* Function should be called prior any actual copy request */
|
||||||
@ -34,6 +35,7 @@ void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm);
|
|||||||
|
|
||||||
void block_copy_state_free(BlockCopyState *s);
|
void block_copy_state_free(BlockCopyState *s);
|
||||||
|
|
||||||
|
void block_copy_reset(BlockCopyState *s, int64_t offset, int64_t bytes);
|
||||||
int64_t block_copy_reset_unallocated(BlockCopyState *s,
|
int64_t block_copy_reset_unallocated(BlockCopyState *s,
|
||||||
int64_t offset, int64_t *count);
|
int64_t offset, int64_t *count);
|
||||||
|
|
||||||
|
@ -597,6 +597,30 @@ struct BlockDriver {
|
|||||||
bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum,
|
bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum,
|
||||||
int64_t *map, BlockDriverState **file);
|
int64_t *map, BlockDriverState **file);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Snapshot-access API.
|
||||||
|
*
|
||||||
|
* Block-driver may provide snapshot-access API: special functions to access
|
||||||
|
* some internal "snapshot". The functions are similar with normal
|
||||||
|
* read/block_status/discard handler, but don't have any specific handling
|
||||||
|
* in generic block-layer: no serializing, no alignment, no tracked
|
||||||
|
* requests. So, block-driver that realizes these APIs is fully responsible
|
||||||
|
* for synchronization between snapshot-access API and normal IO requests.
|
||||||
|
*
|
||||||
|
* TODO: To be able to support qcow2's internal snapshots, this API will
|
||||||
|
* need to be extended to:
|
||||||
|
* - be able to select a specific snapshot
|
||||||
|
* - receive the snapshot's actual length (which may differ from bs's
|
||||||
|
* length)
|
||||||
|
*/
|
||||||
|
int coroutine_fn (*bdrv_co_preadv_snapshot)(BlockDriverState *bs,
|
||||||
|
int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset);
|
||||||
|
int coroutine_fn (*bdrv_co_snapshot_block_status)(BlockDriverState *bs,
|
||||||
|
bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum,
|
||||||
|
int64_t *map, BlockDriverState **file);
|
||||||
|
int coroutine_fn (*bdrv_co_pdiscard_snapshot)(BlockDriverState *bs,
|
||||||
|
int64_t offset, int64_t bytes);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Invalidate any cached meta-data.
|
* Invalidate any cached meta-data.
|
||||||
*/
|
*/
|
||||||
|
@ -33,6 +33,15 @@
|
|||||||
* the I/O API.
|
* the I/O API.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
int coroutine_fn bdrv_co_preadv_snapshot(BdrvChild *child,
|
||||||
|
int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset);
|
||||||
|
int coroutine_fn bdrv_co_snapshot_block_status(BlockDriverState *bs,
|
||||||
|
bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum,
|
||||||
|
int64_t *map, BlockDriverState **file);
|
||||||
|
int coroutine_fn bdrv_co_pdiscard_snapshot(BlockDriverState *bs,
|
||||||
|
int64_t offset, int64_t bytes);
|
||||||
|
|
||||||
|
|
||||||
int coroutine_fn bdrv_co_preadv(BdrvChild *child,
|
int coroutine_fn bdrv_co_preadv(BdrvChild *child,
|
||||||
int64_t offset, int64_t bytes, QEMUIOVector *qiov,
|
int64_t offset, int64_t bytes, QEMUIOVector *qiov,
|
||||||
BdrvRequestFlags flags);
|
BdrvRequestFlags flags);
|
||||||
|
@ -77,7 +77,7 @@ void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap,
|
|||||||
bool persistent);
|
bool persistent);
|
||||||
void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap);
|
void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap);
|
||||||
void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy);
|
void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy);
|
||||||
void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
bool bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src,
|
||||||
HBitmap **backup, Error **errp);
|
HBitmap **backup, Error **errp);
|
||||||
void bdrv_dirty_bitmap_skip_store(BdrvDirtyBitmap *bitmap, bool skip);
|
void bdrv_dirty_bitmap_skip_store(BdrvDirtyBitmap *bitmap, bool skip);
|
||||||
bool bdrv_dirty_bitmap_get(BdrvDirtyBitmap *bitmap, int64_t offset);
|
bool bdrv_dirty_bitmap_get(BdrvDirtyBitmap *bitmap, int64_t offset);
|
||||||
@ -115,6 +115,8 @@ int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, int64_t offset,
|
|||||||
bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
|
bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap,
|
||||||
int64_t start, int64_t end, int64_t max_dirty_count,
|
int64_t start, int64_t end, int64_t max_dirty_count,
|
||||||
int64_t *dirty_start, int64_t *dirty_count);
|
int64_t *dirty_start, int64_t *dirty_count);
|
||||||
|
bool bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap, int64_t offset,
|
||||||
|
int64_t bytes, int64_t *count);
|
||||||
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap,
|
||||||
Error **errp);
|
Error **errp);
|
||||||
|
|
||||||
|
75
include/block/reqlist.h
Normal file
75
include/block/reqlist.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* reqlist API
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Proxmox Server Solutions
|
||||||
|
* Copyright (c) 2021 Virtuozzo International GmbH.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Dietmar Maurer (dietmar@proxmox.com)
|
||||||
|
* Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REQLIST_H
|
||||||
|
#define REQLIST_H
|
||||||
|
|
||||||
|
#include "qemu/coroutine.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The API is not thread-safe and shouldn't be. The struct is public to be part
|
||||||
|
* of other structures and protected by third-party locks, see
|
||||||
|
* block/block-copy.c for example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct BlockReq {
|
||||||
|
int64_t offset;
|
||||||
|
int64_t bytes;
|
||||||
|
|
||||||
|
CoQueue wait_queue; /* coroutines blocked on this req */
|
||||||
|
QLIST_ENTRY(BlockReq) list;
|
||||||
|
} BlockReq;
|
||||||
|
|
||||||
|
typedef QLIST_HEAD(, BlockReq) BlockReqList;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize new request and add it to the list. Caller must be sure that
|
||||||
|
* there are no conflicting requests in the list.
|
||||||
|
*/
|
||||||
|
void reqlist_init_req(BlockReqList *reqs, BlockReq *req, int64_t offset,
|
||||||
|
int64_t bytes);
|
||||||
|
/* Search for request in the list intersecting with @offset/@bytes area. */
|
||||||
|
BlockReq *reqlist_find_conflict(BlockReqList *reqs, int64_t offset,
|
||||||
|
int64_t bytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there are no intersecting requests return false. Otherwise, wait for the
|
||||||
|
* first found intersecting request to finish and return true.
|
||||||
|
*
|
||||||
|
* @lock is passed to qemu_co_queue_wait()
|
||||||
|
* False return value proves that lock was released at no point.
|
||||||
|
*/
|
||||||
|
bool coroutine_fn reqlist_wait_one(BlockReqList *reqs, int64_t offset,
|
||||||
|
int64_t bytes, CoMutex *lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait for all intersecting requests. It just calls reqlist_wait_one() in a
|
||||||
|
* loop, caller is responsible to stop producing new requests in this region
|
||||||
|
* in parallel, otherwise reqlist_wait_all() may never return.
|
||||||
|
*/
|
||||||
|
void coroutine_fn reqlist_wait_all(BlockReqList *reqs, int64_t offset,
|
||||||
|
int64_t bytes, CoMutex *lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shrink request and wake all waiting coroutines (maybe some of them are not
|
||||||
|
* intersecting with shrunk request).
|
||||||
|
*/
|
||||||
|
void coroutine_fn reqlist_shrink_req(BlockReq *req, int64_t new_bytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove request and wake all waiting coroutines. Do not release any memory.
|
||||||
|
*/
|
||||||
|
void coroutine_fn reqlist_remove_req(BlockReq *req);
|
||||||
|
|
||||||
|
#endif /* REQLIST_H */
|
@ -340,6 +340,18 @@ bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end,
|
|||||||
int64_t max_dirty_count,
|
int64_t max_dirty_count,
|
||||||
int64_t *dirty_start, int64_t *dirty_count);
|
int64_t *dirty_start, int64_t *dirty_count);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bdrv_dirty_bitmap_status:
|
||||||
|
* @hb: The HBitmap to operate on
|
||||||
|
* @start: The bit to start from
|
||||||
|
* @count: Number of bits to proceed
|
||||||
|
* @pnum: Out-parameter. How many bits has same value starting from @start
|
||||||
|
*
|
||||||
|
* Returns true if bitmap is dirty at @start, false otherwise.
|
||||||
|
*/
|
||||||
|
bool hbitmap_status(const HBitmap *hb, int64_t start, int64_t count,
|
||||||
|
int64_t *pnum);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* hbitmap_iter_next:
|
* hbitmap_iter_next:
|
||||||
* @hbi: HBitmapIter to operate on.
|
* @hbi: HBitmapIter to operate on.
|
||||||
|
@ -2914,13 +2914,14 @@
|
|||||||
# @blkreplay: Since 4.2
|
# @blkreplay: Since 4.2
|
||||||
# @compress: Since 5.0
|
# @compress: Since 5.0
|
||||||
# @copy-before-write: Since 6.2
|
# @copy-before-write: Since 6.2
|
||||||
|
# @snapshot-access: Since 7.0
|
||||||
#
|
#
|
||||||
# Since: 2.9
|
# Since: 2.9
|
||||||
##
|
##
|
||||||
{ 'enum': 'BlockdevDriver',
|
{ 'enum': 'BlockdevDriver',
|
||||||
'data': [ 'blkdebug', 'blklogwrites', 'blkreplay', 'blkverify', 'bochs',
|
'data': [ 'blkdebug', 'blklogwrites', 'blkreplay', 'blkverify', 'bochs',
|
||||||
'cloop', 'compress', 'copy-before-write', 'copy-on-read', 'dmg',
|
'cloop', 'compress', 'copy-before-write', 'copy-on-read', 'dmg',
|
||||||
'file', 'ftp', 'ftps', 'gluster',
|
'file', 'snapshot-access', 'ftp', 'ftps', 'gluster',
|
||||||
{'name': 'host_cdrom', 'if': 'HAVE_HOST_BLOCK_DEVICE' },
|
{'name': 'host_cdrom', 'if': 'HAVE_HOST_BLOCK_DEVICE' },
|
||||||
{'name': 'host_device', 'if': 'HAVE_HOST_BLOCK_DEVICE' },
|
{'name': 'host_device', 'if': 'HAVE_HOST_BLOCK_DEVICE' },
|
||||||
'http', 'https', 'iscsi',
|
'http', 'https', 'iscsi',
|
||||||
@ -4171,11 +4172,19 @@
|
|||||||
#
|
#
|
||||||
# @target: The target for copy-before-write operations.
|
# @target: The target for copy-before-write operations.
|
||||||
#
|
#
|
||||||
|
# @bitmap: If specified, copy-before-write filter will do
|
||||||
|
# copy-before-write operations only for dirty regions of the
|
||||||
|
# bitmap. Bitmap size must be equal to length of file and
|
||||||
|
# target child of the filter. Note also, that bitmap is used
|
||||||
|
# only to initialize internal bitmap of the process, so further
|
||||||
|
# modifications (or removing) of specified bitmap doesn't
|
||||||
|
# influence the filter. (Since 7.0)
|
||||||
|
#
|
||||||
# Since: 6.2
|
# Since: 6.2
|
||||||
##
|
##
|
||||||
{ 'struct': 'BlockdevOptionsCbw',
|
{ 'struct': 'BlockdevOptionsCbw',
|
||||||
'base': 'BlockdevOptionsGenericFormat',
|
'base': 'BlockdevOptionsGenericFormat',
|
||||||
'data': { 'target': 'BlockdevRef' } }
|
'data': { 'target': 'BlockdevRef', '*bitmap': 'BlockDirtyBitmap' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockdevOptions:
|
# @BlockdevOptions:
|
||||||
@ -4259,6 +4268,7 @@
|
|||||||
'rbd': 'BlockdevOptionsRbd',
|
'rbd': 'BlockdevOptionsRbd',
|
||||||
'replication': { 'type': 'BlockdevOptionsReplication',
|
'replication': { 'type': 'BlockdevOptionsReplication',
|
||||||
'if': 'CONFIG_REPLICATION' },
|
'if': 'CONFIG_REPLICATION' },
|
||||||
|
'snapshot-access': 'BlockdevOptionsGenericFormat',
|
||||||
'ssh': 'BlockdevOptionsSsh',
|
'ssh': 'BlockdevOptionsSsh',
|
||||||
'throttle': 'BlockdevOptionsThrottle',
|
'throttle': 'BlockdevOptionsThrottle',
|
||||||
'vdi': 'BlockdevOptionsGenericFormat',
|
'vdi': 'BlockdevOptionsGenericFormat',
|
||||||
|
@ -744,6 +744,7 @@ class TestCommitWithFilters(iotests.QMPTestCase):
|
|||||||
pattern_file)
|
pattern_file)
|
||||||
self.assertFalse('Pattern verification failed' in result)
|
self.assertFalse('Pattern verification failed' in result)
|
||||||
|
|
||||||
|
@iotests.skip_if_unsupported(['throttle'])
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
qemu_img('create', '-f', iotests.imgfmt, self.img0, '64M')
|
qemu_img('create', '-f', iotests.imgfmt, self.img0, '64M')
|
||||||
qemu_img('create', '-f', iotests.imgfmt, self.img1, '64M')
|
qemu_img('create', '-f', iotests.imgfmt, self.img1, '64M')
|
||||||
|
@ -106,6 +106,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -566,6 +582,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -819,6 +851,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -1279,6 +1327,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -1532,6 +1596,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -1992,6 +2072,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -2245,6 +2341,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -2705,6 +2817,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -2958,6 +3086,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -3418,6 +3562,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -3671,6 +3831,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -4131,6 +4307,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -4384,6 +4576,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
@ -4844,6 +5052,22 @@ write -P0x67 0x3fe0000 0x20000
|
|||||||
{"return": ""}
|
{"return": ""}
|
||||||
{
|
{
|
||||||
"bitmaps": {
|
"bitmaps": {
|
||||||
|
"backup-top": [
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 67108864,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"busy": false,
|
||||||
|
"count": 458752,
|
||||||
|
"granularity": 65536,
|
||||||
|
"persistent": false,
|
||||||
|
"recording": false
|
||||||
|
}
|
||||||
|
],
|
||||||
"drive0": [
|
"drive0": [
|
||||||
{
|
{
|
||||||
"busy": false,
|
"busy": false,
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
# bail out, setting up .notrun file
|
# bail out, setting up .notrun file
|
||||||
_notrun()
|
_notrun()
|
||||||
{
|
{
|
||||||
echo "$*" >"$OUTPUT_DIR/$seq.notrun"
|
echo "$*" >"$TEST_DIR/$seq.notrun"
|
||||||
echo "$seq not run: $*"
|
echo "$seq not run: $*"
|
||||||
status=0
|
status=0
|
||||||
exit
|
exit
|
||||||
@ -739,14 +739,14 @@ _img_info()
|
|||||||
#
|
#
|
||||||
_casenotrun()
|
_casenotrun()
|
||||||
{
|
{
|
||||||
echo " [case not run] $*" >>"$OUTPUT_DIR/$seq.casenotrun"
|
echo " [case not run] $*" >>"$TEST_DIR/$seq.casenotrun"
|
||||||
}
|
}
|
||||||
|
|
||||||
# just plain bail out
|
# just plain bail out
|
||||||
#
|
#
|
||||||
_fail()
|
_fail()
|
||||||
{
|
{
|
||||||
echo "$*" | tee -a "$OUTPUT_DIR/$seq.full"
|
echo "$*" | tee -a "$TEST_DIR/$seq.full"
|
||||||
echo "(see $seq.full for details)"
|
echo "(see $seq.full for details)"
|
||||||
status=1
|
status=1
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -85,7 +85,6 @@ qemu_print = os.environ.get('PRINT_QEMU', False)
|
|||||||
|
|
||||||
imgfmt = os.environ.get('IMGFMT', 'raw')
|
imgfmt = os.environ.get('IMGFMT', 'raw')
|
||||||
imgproto = os.environ.get('IMGPROTO', 'file')
|
imgproto = os.environ.get('IMGPROTO', 'file')
|
||||||
output_dir = os.environ.get('OUTPUT_DIR', '.')
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
test_dir = os.environ['TEST_DIR']
|
test_dir = os.environ['TEST_DIR']
|
||||||
@ -279,6 +278,9 @@ def qemu_io(*args):
|
|||||||
'''Run qemu-io and return the stdout data'''
|
'''Run qemu-io and return the stdout data'''
|
||||||
return qemu_tool_pipe_and_status('qemu-io', qemu_io_wrap_args(args))[0]
|
return qemu_tool_pipe_and_status('qemu-io', qemu_io_wrap_args(args))[0]
|
||||||
|
|
||||||
|
def qemu_io_pipe_and_status(*args):
|
||||||
|
return qemu_tool_pipe_and_status('qemu-io', qemu_io_wrap_args(args))
|
||||||
|
|
||||||
def qemu_io_log(*args):
|
def qemu_io_log(*args):
|
||||||
result = qemu_io(*args)
|
result = qemu_io(*args)
|
||||||
log(result, filters=[filter_testfiles, filter_qemu_io])
|
log(result, filters=[filter_testfiles, filter_qemu_io])
|
||||||
@ -1239,7 +1241,7 @@ def notrun(reason):
|
|||||||
# Each test in qemu-iotests has a number ("seq")
|
# Each test in qemu-iotests has a number ("seq")
|
||||||
seq = os.path.basename(sys.argv[0])
|
seq = os.path.basename(sys.argv[0])
|
||||||
|
|
||||||
with open('%s/%s.notrun' % (output_dir, seq), 'w', encoding='utf-8') \
|
with open('%s/%s.notrun' % (test_dir, seq), 'w', encoding='utf-8') \
|
||||||
as outfile:
|
as outfile:
|
||||||
outfile.write(reason + '\n')
|
outfile.write(reason + '\n')
|
||||||
logger.warning("%s not run: %s", seq, reason)
|
logger.warning("%s not run: %s", seq, reason)
|
||||||
@ -1254,7 +1256,7 @@ def case_notrun(reason):
|
|||||||
# Each test in qemu-iotests has a number ("seq")
|
# Each test in qemu-iotests has a number ("seq")
|
||||||
seq = os.path.basename(sys.argv[0])
|
seq = os.path.basename(sys.argv[0])
|
||||||
|
|
||||||
with open('%s/%s.casenotrun' % (output_dir, seq), 'a', encoding='utf-8') \
|
with open('%s/%s.casenotrun' % (test_dir, seq), 'a', encoding='utf-8') \
|
||||||
as outfile:
|
as outfile:
|
||||||
outfile.write(' [case not run] ' + reason + '\n')
|
outfile.write(' [case not run] ' + reason + '\n')
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ class TestEnv(ContextManager['TestEnv']):
|
|||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
|
|
||||||
env_variables = ['PYTHONPATH', 'TEST_DIR', 'SOCK_DIR', 'SAMPLE_IMG_DIR',
|
env_variables = ['PYTHONPATH', 'TEST_DIR', 'SOCK_DIR', 'SAMPLE_IMG_DIR',
|
||||||
'OUTPUT_DIR', 'PYTHON', 'QEMU_PROG', 'QEMU_IMG_PROG',
|
'PYTHON', 'QEMU_PROG', 'QEMU_IMG_PROG',
|
||||||
'QEMU_IO_PROG', 'QEMU_NBD_PROG', 'QSD_PROG',
|
'QEMU_IO_PROG', 'QEMU_NBD_PROG', 'QSD_PROG',
|
||||||
'QEMU_OPTIONS', 'QEMU_IMG_OPTIONS',
|
'QEMU_OPTIONS', 'QEMU_IMG_OPTIONS',
|
||||||
'QEMU_IO_OPTIONS', 'QEMU_IO_OPTIONS_NO_FMT',
|
'QEMU_IO_OPTIONS', 'QEMU_IO_OPTIONS_NO_FMT',
|
||||||
@ -106,7 +106,6 @@ class TestEnv(ContextManager['TestEnv']):
|
|||||||
TEST_DIR
|
TEST_DIR
|
||||||
SOCK_DIR
|
SOCK_DIR
|
||||||
SAMPLE_IMG_DIR
|
SAMPLE_IMG_DIR
|
||||||
OUTPUT_DIR
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Path where qemu goodies live in this source tree.
|
# Path where qemu goodies live in this source tree.
|
||||||
@ -134,8 +133,6 @@ class TestEnv(ContextManager['TestEnv']):
|
|||||||
os.path.join(self.source_iotests,
|
os.path.join(self.source_iotests,
|
||||||
'sample_images'))
|
'sample_images'))
|
||||||
|
|
||||||
self.output_dir = os.getcwd() # OUTPUT_DIR
|
|
||||||
|
|
||||||
def init_binaries(self) -> None:
|
def init_binaries(self) -> None:
|
||||||
"""Init binary path variables:
|
"""Init binary path variables:
|
||||||
PYTHON (for bash tests)
|
PYTHON (for bash tests)
|
||||||
|
@ -259,9 +259,6 @@ class TestRunner(ContextManager['TestRunner']):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
f_test = Path(test)
|
f_test = Path(test)
|
||||||
f_bad = Path(f_test.name + '.out.bad')
|
|
||||||
f_notrun = Path(f_test.name + '.notrun')
|
|
||||||
f_casenotrun = Path(f_test.name + '.casenotrun')
|
|
||||||
f_reference = Path(self.find_reference(test))
|
f_reference = Path(self.find_reference(test))
|
||||||
|
|
||||||
if not f_test.exists():
|
if not f_test.exists():
|
||||||
@ -276,9 +273,6 @@ class TestRunner(ContextManager['TestRunner']):
|
|||||||
description='No qualified output '
|
description='No qualified output '
|
||||||
f'(expected {f_reference})')
|
f'(expected {f_reference})')
|
||||||
|
|
||||||
for p in (f_bad, f_notrun, f_casenotrun):
|
|
||||||
silent_unlink(p)
|
|
||||||
|
|
||||||
args = [str(f_test.resolve())]
|
args = [str(f_test.resolve())]
|
||||||
env = self.env.prepare_subprocess(args)
|
env = self.env.prepare_subprocess(args)
|
||||||
if mp:
|
if mp:
|
||||||
@ -288,6 +282,14 @@ class TestRunner(ContextManager['TestRunner']):
|
|||||||
env[d] = os.path.join(env[d], f_test.name)
|
env[d] = os.path.join(env[d], f_test.name)
|
||||||
Path(env[d]).mkdir(parents=True, exist_ok=True)
|
Path(env[d]).mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
test_dir = env['TEST_DIR']
|
||||||
|
f_bad = Path(test_dir, f_test.name + '.out.bad')
|
||||||
|
f_notrun = Path(test_dir, f_test.name + '.notrun')
|
||||||
|
f_casenotrun = Path(test_dir, f_test.name + '.casenotrun')
|
||||||
|
|
||||||
|
for p in (f_notrun, f_casenotrun):
|
||||||
|
silent_unlink(p)
|
||||||
|
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
with f_bad.open('w', encoding="utf-8") as f:
|
with f_bad.open('w', encoding="utf-8") as f:
|
||||||
with subprocess.Popen(args, cwd=str(f_test.parent), env=env,
|
with subprocess.Popen(args, cwd=str(f_test.parent), env=env,
|
||||||
@ -365,7 +367,10 @@ class TestRunner(ContextManager['TestRunner']):
|
|||||||
description=res.description)
|
description=res.description)
|
||||||
|
|
||||||
if res.casenotrun:
|
if res.casenotrun:
|
||||||
print(res.casenotrun)
|
if self.tap:
|
||||||
|
print('#' + res.casenotrun.replace('\n', '\n#'))
|
||||||
|
else:
|
||||||
|
print(res.casenotrun)
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
@ -23,12 +23,14 @@
|
|||||||
# Creator/Owner: John Snow <jsnow@redhat.com>
|
# Creator/Owner: John Snow <jsnow@redhat.com>
|
||||||
|
|
||||||
import iotests
|
import iotests
|
||||||
from iotests import log, qemu_img, qemu_io, qemu_io_silent
|
from iotests import log, qemu_img, qemu_io, qemu_io_silent, \
|
||||||
|
qemu_io_pipe_and_status
|
||||||
|
|
||||||
iotests.script_initialize(
|
iotests.script_initialize(
|
||||||
supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', 'vhdx', 'raw'],
|
supported_fmts=['qcow2'],
|
||||||
supported_platforms=['linux'],
|
supported_platforms=['linux'],
|
||||||
required_fmts=['copy-before-write'],
|
required_fmts=['copy-before-write'],
|
||||||
|
unsupported_imgopts=['compat']
|
||||||
)
|
)
|
||||||
|
|
||||||
patterns = [('0x5d', '0', '64k'),
|
patterns = [('0x5d', '0', '64k'),
|
||||||
@ -49,12 +51,30 @@ remainder = [('0xd5', '0x108000', '32k'), # Right-end of partial-left [1]
|
|||||||
('0xdc', '32M', '32k'), # Left-end of partial-right [2]
|
('0xdc', '32M', '32k'), # Left-end of partial-right [2]
|
||||||
('0xcd', '0x3ff0000', '64k')] # patterns[3]
|
('0xcd', '0x3ff0000', '64k')] # patterns[3]
|
||||||
|
|
||||||
def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm):
|
def do_test(vm, use_cbw, use_snapshot_access_filter, base_img_path,
|
||||||
|
fleece_img_path, nbd_sock_path=None,
|
||||||
|
target_img_path=None,
|
||||||
|
bitmap=False):
|
||||||
|
push_backup = target_img_path is not None
|
||||||
|
assert (nbd_sock_path is not None) != push_backup
|
||||||
|
if push_backup:
|
||||||
|
assert use_cbw
|
||||||
|
|
||||||
log('--- Setting up images ---')
|
log('--- Setting up images ---')
|
||||||
log('')
|
log('')
|
||||||
|
|
||||||
assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
|
assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0
|
||||||
assert qemu_img('create', '-f', 'qcow2', fleece_img_path, '64M') == 0
|
if bitmap:
|
||||||
|
assert qemu_img('bitmap', '--add', base_img_path, 'bitmap0') == 0
|
||||||
|
|
||||||
|
if use_snapshot_access_filter:
|
||||||
|
assert use_cbw
|
||||||
|
assert qemu_img('create', '-f', 'raw', fleece_img_path, '64M') == 0
|
||||||
|
else:
|
||||||
|
assert qemu_img('create', '-f', 'qcow2', fleece_img_path, '64M') == 0
|
||||||
|
|
||||||
|
if push_backup:
|
||||||
|
assert qemu_img('create', '-f', 'qcow2', target_img_path, '64M') == 0
|
||||||
|
|
||||||
for p in patterns:
|
for p in patterns:
|
||||||
qemu_io('-f', iotests.imgfmt,
|
qemu_io('-f', iotests.imgfmt,
|
||||||
@ -81,27 +101,46 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm):
|
|||||||
log('')
|
log('')
|
||||||
|
|
||||||
|
|
||||||
# create tmp_node backed by src_node
|
if use_snapshot_access_filter:
|
||||||
log(vm.qmp('blockdev-add', {
|
log(vm.qmp('blockdev-add', {
|
||||||
'driver': 'qcow2',
|
'node-name': tmp_node,
|
||||||
'node-name': tmp_node,
|
|
||||||
'file': {
|
|
||||||
'driver': 'file',
|
'driver': 'file',
|
||||||
'filename': fleece_img_path,
|
'filename': fleece_img_path,
|
||||||
},
|
}))
|
||||||
'backing': src_node,
|
else:
|
||||||
}))
|
# create tmp_node backed by src_node
|
||||||
|
log(vm.qmp('blockdev-add', {
|
||||||
|
'driver': 'qcow2',
|
||||||
|
'node-name': tmp_node,
|
||||||
|
'file': {
|
||||||
|
'driver': 'file',
|
||||||
|
'filename': fleece_img_path,
|
||||||
|
},
|
||||||
|
'backing': src_node,
|
||||||
|
}))
|
||||||
|
|
||||||
# Establish CBW from source to fleecing node
|
# Establish CBW from source to fleecing node
|
||||||
if use_cbw:
|
if use_cbw:
|
||||||
log(vm.qmp('blockdev-add', {
|
fl_cbw = {
|
||||||
'driver': 'copy-before-write',
|
'driver': 'copy-before-write',
|
||||||
'node-name': 'fl-cbw',
|
'node-name': 'fl-cbw',
|
||||||
'file': src_node,
|
'file': src_node,
|
||||||
'target': tmp_node
|
'target': tmp_node
|
||||||
}))
|
}
|
||||||
|
|
||||||
|
if bitmap:
|
||||||
|
fl_cbw['bitmap'] = {'node': src_node, 'name': 'bitmap0'}
|
||||||
|
|
||||||
|
log(vm.qmp('blockdev-add', fl_cbw))
|
||||||
|
|
||||||
log(vm.qmp('qom-set', path=qom_path, property='drive', value='fl-cbw'))
|
log(vm.qmp('qom-set', path=qom_path, property='drive', value='fl-cbw'))
|
||||||
|
|
||||||
|
if use_snapshot_access_filter:
|
||||||
|
log(vm.qmp('blockdev-add', {
|
||||||
|
'driver': 'snapshot-access',
|
||||||
|
'node-name': 'fl-access',
|
||||||
|
'file': 'fl-cbw',
|
||||||
|
}))
|
||||||
else:
|
else:
|
||||||
log(vm.qmp('blockdev-backup',
|
log(vm.qmp('blockdev-backup',
|
||||||
job_id='fleecing',
|
job_id='fleecing',
|
||||||
@ -109,25 +148,47 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm):
|
|||||||
target=tmp_node,
|
target=tmp_node,
|
||||||
sync='none'))
|
sync='none'))
|
||||||
|
|
||||||
log('')
|
export_node = 'fl-access' if use_snapshot_access_filter else tmp_node
|
||||||
log('--- Setting up NBD Export ---')
|
|
||||||
log('')
|
|
||||||
|
|
||||||
nbd_uri = 'nbd+unix:///%s?socket=%s' % (tmp_node, nbd_sock_path)
|
if push_backup:
|
||||||
log(vm.qmp('nbd-server-start',
|
log('')
|
||||||
{'addr': {'type': 'unix',
|
log('--- Starting actual backup ---')
|
||||||
'data': {'path': nbd_sock_path}}}))
|
log('')
|
||||||
|
|
||||||
log(vm.qmp('nbd-server-add', device=tmp_node))
|
log(vm.qmp('blockdev-add', **{
|
||||||
|
'driver': iotests.imgfmt,
|
||||||
|
'node-name': 'target',
|
||||||
|
'file': {
|
||||||
|
'driver': 'file',
|
||||||
|
'filename': target_img_path
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
log(vm.qmp('blockdev-backup', device=export_node,
|
||||||
|
sync='full', target='target',
|
||||||
|
job_id='push-backup', speed=1))
|
||||||
|
else:
|
||||||
|
log('')
|
||||||
|
log('--- Setting up NBD Export ---')
|
||||||
|
log('')
|
||||||
|
|
||||||
log('')
|
nbd_uri = 'nbd+unix:///%s?socket=%s' % (export_node, nbd_sock_path)
|
||||||
log('--- Sanity Check ---')
|
log(vm.qmp('nbd-server-start',
|
||||||
log('')
|
{'addr': { 'type': 'unix',
|
||||||
|
'data': { 'path': nbd_sock_path } } }))
|
||||||
|
|
||||||
for p in patterns + zeroes:
|
log(vm.qmp('nbd-server-add', device=export_node))
|
||||||
cmd = 'read -P%s %s %s' % p
|
|
||||||
log(cmd)
|
log('')
|
||||||
assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0
|
log('--- Sanity Check ---')
|
||||||
|
log('')
|
||||||
|
|
||||||
|
for p in patterns + zeroes:
|
||||||
|
cmd = 'read -P%s %s %s' % p
|
||||||
|
log(cmd)
|
||||||
|
out, ret = qemu_io_pipe_and_status('-r', '-f', 'raw', '-c', cmd,
|
||||||
|
nbd_uri)
|
||||||
|
if ret != 0:
|
||||||
|
print(out)
|
||||||
|
|
||||||
log('')
|
log('')
|
||||||
log('--- Testing COW ---')
|
log('--- Testing COW ---')
|
||||||
@ -138,6 +199,23 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm):
|
|||||||
log(cmd)
|
log(cmd)
|
||||||
log(vm.hmp_qemu_io(qom_path, cmd, qdev=True))
|
log(vm.hmp_qemu_io(qom_path, cmd, qdev=True))
|
||||||
|
|
||||||
|
if push_backup:
|
||||||
|
# Check that previous operations were done during backup, not after
|
||||||
|
# If backup is already finished, it's possible that it was finished
|
||||||
|
# even before hmp qemu_io write, and we didn't actually test
|
||||||
|
# copy-before-write operation. This should not happen, as we use
|
||||||
|
# speed=1. But worth checking.
|
||||||
|
result = vm.qmp('query-block-jobs')
|
||||||
|
assert len(result['return']) == 1
|
||||||
|
|
||||||
|
result = vm.qmp('block-job-set-speed', device='push-backup', speed=0)
|
||||||
|
assert result == {'return': {}}
|
||||||
|
|
||||||
|
log(vm.event_wait(name='BLOCK_JOB_COMPLETED',
|
||||||
|
match={'data': {'device': 'push-backup'}}),
|
||||||
|
filters=[iotests.filter_qmp_event])
|
||||||
|
log(vm.qmp('blockdev-del', node_name='target'))
|
||||||
|
|
||||||
log('')
|
log('')
|
||||||
log('--- Verifying Data ---')
|
log('--- Verifying Data ---')
|
||||||
log('')
|
log('')
|
||||||
@ -145,13 +223,25 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm):
|
|||||||
for p in patterns + zeroes:
|
for p in patterns + zeroes:
|
||||||
cmd = 'read -P%s %s %s' % p
|
cmd = 'read -P%s %s %s' % p
|
||||||
log(cmd)
|
log(cmd)
|
||||||
assert qemu_io_silent('-r', '-f', 'raw', '-c', cmd, nbd_uri) == 0
|
args = ['-r', '-c', cmd]
|
||||||
|
if push_backup:
|
||||||
|
args += [target_img_path]
|
||||||
|
else:
|
||||||
|
args += ['-f', 'raw', nbd_uri]
|
||||||
|
out, ret = qemu_io_pipe_and_status(*args)
|
||||||
|
if ret != 0:
|
||||||
|
print(out)
|
||||||
|
|
||||||
log('')
|
log('')
|
||||||
log('--- Cleanup ---')
|
log('--- Cleanup ---')
|
||||||
log('')
|
log('')
|
||||||
|
|
||||||
|
if not push_backup:
|
||||||
|
log(vm.qmp('nbd-server-stop'))
|
||||||
|
|
||||||
if use_cbw:
|
if use_cbw:
|
||||||
|
if use_snapshot_access_filter:
|
||||||
|
log(vm.qmp('blockdev-del', node_name='fl-access'))
|
||||||
log(vm.qmp('qom-set', path=qom_path, property='drive', value=src_node))
|
log(vm.qmp('qom-set', path=qom_path, property='drive', value=src_node))
|
||||||
log(vm.qmp('blockdev-del', node_name='fl-cbw'))
|
log(vm.qmp('blockdev-del', node_name='fl-cbw'))
|
||||||
else:
|
else:
|
||||||
@ -160,7 +250,6 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm):
|
|||||||
assert e is not None
|
assert e is not None
|
||||||
log(e, filters=[iotests.filter_qmp_event])
|
log(e, filters=[iotests.filter_qmp_event])
|
||||||
|
|
||||||
log(vm.qmp('nbd-server-stop'))
|
|
||||||
log(vm.qmp('blockdev-del', node_name=tmp_node))
|
log(vm.qmp('blockdev-del', node_name=tmp_node))
|
||||||
vm.shutdown()
|
vm.shutdown()
|
||||||
|
|
||||||
@ -177,17 +266,37 @@ def do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm):
|
|||||||
log('Done')
|
log('Done')
|
||||||
|
|
||||||
|
|
||||||
def test(use_cbw):
|
def test(use_cbw, use_snapshot_access_filter,
|
||||||
|
nbd_sock_path=None, target_img_path=None, bitmap=False):
|
||||||
with iotests.FilePath('base.img') as base_img_path, \
|
with iotests.FilePath('base.img') as base_img_path, \
|
||||||
iotests.FilePath('fleece.img') as fleece_img_path, \
|
iotests.FilePath('fleece.img') as fleece_img_path, \
|
||||||
iotests.FilePath('nbd.sock',
|
|
||||||
base_dir=iotests.sock_dir) as nbd_sock_path, \
|
|
||||||
iotests.VM() as vm:
|
iotests.VM() as vm:
|
||||||
do_test(use_cbw, base_img_path, fleece_img_path, nbd_sock_path, vm)
|
do_test(vm, use_cbw, use_snapshot_access_filter, base_img_path,
|
||||||
|
fleece_img_path, nbd_sock_path, target_img_path,
|
||||||
|
bitmap=bitmap)
|
||||||
|
|
||||||
|
def test_pull(use_cbw, use_snapshot_access_filter, bitmap=False):
|
||||||
|
with iotests.FilePath('nbd.sock',
|
||||||
|
base_dir=iotests.sock_dir) as nbd_sock_path:
|
||||||
|
test(use_cbw, use_snapshot_access_filter, nbd_sock_path, None,
|
||||||
|
bitmap=bitmap)
|
||||||
|
|
||||||
|
def test_push():
|
||||||
|
with iotests.FilePath('target.img') as target_img_path:
|
||||||
|
test(True, True, None, target_img_path)
|
||||||
|
|
||||||
|
|
||||||
log('=== Test backup(sync=none) based fleecing ===\n')
|
log('=== Test backup(sync=none) based fleecing ===\n')
|
||||||
test(False)
|
test_pull(False, False)
|
||||||
|
|
||||||
log('=== Test filter based fleecing ===\n')
|
log('=== Test cbw-filter based fleecing ===\n')
|
||||||
test(True)
|
test_pull(True, False)
|
||||||
|
|
||||||
|
log('=== Test fleecing-format based fleecing ===\n')
|
||||||
|
test_pull(True, True)
|
||||||
|
|
||||||
|
log('=== Test fleecing-format based fleecing with bitmap ===\n')
|
||||||
|
test_pull(True, True, bitmap=True)
|
||||||
|
|
||||||
|
log('=== Test push backup with fleecing ===\n')
|
||||||
|
test_push()
|
||||||
|
@ -52,8 +52,8 @@ read -P0 0x3fe0000 64k
|
|||||||
--- Cleanup ---
|
--- Cleanup ---
|
||||||
|
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
{"data": {"device": "fleecing", "len": 67108864, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
|
{"data": {"device": "fleecing", "len": 67108864, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||||
{"return": {}}
|
{"return": {}}
|
||||||
|
|
||||||
--- Confirming writes ---
|
--- Confirming writes ---
|
||||||
@ -67,7 +67,7 @@ read -P0xdc 32M 32k
|
|||||||
read -P0xcd 0x3ff0000 64k
|
read -P0xcd 0x3ff0000 64k
|
||||||
|
|
||||||
Done
|
Done
|
||||||
=== Test filter based fleecing ===
|
=== Test cbw-filter based fleecing ===
|
||||||
|
|
||||||
--- Setting up images ---
|
--- Setting up images ---
|
||||||
|
|
||||||
@ -137,3 +137,222 @@ read -P0xdc 32M 32k
|
|||||||
read -P0xcd 0x3ff0000 64k
|
read -P0xcd 0x3ff0000 64k
|
||||||
|
|
||||||
Done
|
Done
|
||||||
|
=== Test fleecing-format based fleecing ===
|
||||||
|
|
||||||
|
--- Setting up images ---
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
--- Launching VM ---
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
--- Setting up Fleecing Graph ---
|
||||||
|
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
|
||||||
|
--- Setting up NBD Export ---
|
||||||
|
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
|
||||||
|
--- Sanity Check ---
|
||||||
|
|
||||||
|
read -P0x5d 0 64k
|
||||||
|
read -P0xd5 1M 64k
|
||||||
|
read -P0xdc 32M 64k
|
||||||
|
read -P0xcd 0x3ff0000 64k
|
||||||
|
read -P0 0x00f8000 32k
|
||||||
|
read -P0 0x2010000 32k
|
||||||
|
read -P0 0x3fe0000 64k
|
||||||
|
|
||||||
|
--- Testing COW ---
|
||||||
|
|
||||||
|
write -P0xab 0 64k
|
||||||
|
{"return": ""}
|
||||||
|
write -P0xad 0x00f8000 64k
|
||||||
|
{"return": ""}
|
||||||
|
write -P0x1d 0x2008000 64k
|
||||||
|
{"return": ""}
|
||||||
|
write -P0xea 0x3fe0000 64k
|
||||||
|
{"return": ""}
|
||||||
|
|
||||||
|
--- Verifying Data ---
|
||||||
|
|
||||||
|
read -P0x5d 0 64k
|
||||||
|
read -P0xd5 1M 64k
|
||||||
|
read -P0xdc 32M 64k
|
||||||
|
read -P0xcd 0x3ff0000 64k
|
||||||
|
read -P0 0x00f8000 32k
|
||||||
|
read -P0 0x2010000 32k
|
||||||
|
read -P0 0x3fe0000 64k
|
||||||
|
|
||||||
|
--- Cleanup ---
|
||||||
|
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
|
||||||
|
--- Confirming writes ---
|
||||||
|
|
||||||
|
read -P0xab 0 64k
|
||||||
|
read -P0xad 0x00f8000 64k
|
||||||
|
read -P0x1d 0x2008000 64k
|
||||||
|
read -P0xea 0x3fe0000 64k
|
||||||
|
read -P0xd5 0x108000 32k
|
||||||
|
read -P0xdc 32M 32k
|
||||||
|
read -P0xcd 0x3ff0000 64k
|
||||||
|
|
||||||
|
Done
|
||||||
|
=== Test fleecing-format based fleecing with bitmap ===
|
||||||
|
|
||||||
|
--- Setting up images ---
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
--- Launching VM ---
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
--- Setting up Fleecing Graph ---
|
||||||
|
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
|
||||||
|
--- Setting up NBD Export ---
|
||||||
|
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
|
||||||
|
--- Sanity Check ---
|
||||||
|
|
||||||
|
read -P0x5d 0 64k
|
||||||
|
read -P0xd5 1M 64k
|
||||||
|
read -P0xdc 32M 64k
|
||||||
|
read -P0xcd 0x3ff0000 64k
|
||||||
|
read -P0 0x00f8000 32k
|
||||||
|
read failed: Invalid argument
|
||||||
|
|
||||||
|
read -P0 0x2010000 32k
|
||||||
|
read failed: Invalid argument
|
||||||
|
|
||||||
|
read -P0 0x3fe0000 64k
|
||||||
|
read failed: Invalid argument
|
||||||
|
|
||||||
|
|
||||||
|
--- Testing COW ---
|
||||||
|
|
||||||
|
write -P0xab 0 64k
|
||||||
|
{"return": ""}
|
||||||
|
write -P0xad 0x00f8000 64k
|
||||||
|
{"return": ""}
|
||||||
|
write -P0x1d 0x2008000 64k
|
||||||
|
{"return": ""}
|
||||||
|
write -P0xea 0x3fe0000 64k
|
||||||
|
{"return": ""}
|
||||||
|
|
||||||
|
--- Verifying Data ---
|
||||||
|
|
||||||
|
read -P0x5d 0 64k
|
||||||
|
read -P0xd5 1M 64k
|
||||||
|
read -P0xdc 32M 64k
|
||||||
|
read -P0xcd 0x3ff0000 64k
|
||||||
|
read -P0 0x00f8000 32k
|
||||||
|
read failed: Invalid argument
|
||||||
|
|
||||||
|
read -P0 0x2010000 32k
|
||||||
|
read failed: Invalid argument
|
||||||
|
|
||||||
|
read -P0 0x3fe0000 64k
|
||||||
|
read failed: Invalid argument
|
||||||
|
|
||||||
|
|
||||||
|
--- Cleanup ---
|
||||||
|
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
|
||||||
|
--- Confirming writes ---
|
||||||
|
|
||||||
|
read -P0xab 0 64k
|
||||||
|
read -P0xad 0x00f8000 64k
|
||||||
|
read -P0x1d 0x2008000 64k
|
||||||
|
read -P0xea 0x3fe0000 64k
|
||||||
|
read -P0xd5 0x108000 32k
|
||||||
|
read -P0xdc 32M 32k
|
||||||
|
read -P0xcd 0x3ff0000 64k
|
||||||
|
|
||||||
|
Done
|
||||||
|
=== Test push backup with fleecing ===
|
||||||
|
|
||||||
|
--- Setting up images ---
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
--- Launching VM ---
|
||||||
|
|
||||||
|
Done
|
||||||
|
|
||||||
|
--- Setting up Fleecing Graph ---
|
||||||
|
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
|
||||||
|
--- Starting actual backup ---
|
||||||
|
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
|
||||||
|
--- Testing COW ---
|
||||||
|
|
||||||
|
write -P0xab 0 64k
|
||||||
|
{"return": ""}
|
||||||
|
write -P0xad 0x00f8000 64k
|
||||||
|
{"return": ""}
|
||||||
|
write -P0x1d 0x2008000 64k
|
||||||
|
{"return": ""}
|
||||||
|
write -P0xea 0x3fe0000 64k
|
||||||
|
{"return": ""}
|
||||||
|
{"data": {"device": "push-backup", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
|
||||||
|
{"return": {}}
|
||||||
|
|
||||||
|
--- Verifying Data ---
|
||||||
|
|
||||||
|
read -P0x5d 0 64k
|
||||||
|
read -P0xd5 1M 64k
|
||||||
|
read -P0xdc 32M 64k
|
||||||
|
read -P0xcd 0x3ff0000 64k
|
||||||
|
read -P0 0x00f8000 32k
|
||||||
|
read -P0 0x2010000 32k
|
||||||
|
read -P0 0x3fe0000 64k
|
||||||
|
|
||||||
|
--- Cleanup ---
|
||||||
|
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
{"return": {}}
|
||||||
|
|
||||||
|
--- Confirming writes ---
|
||||||
|
|
||||||
|
read -P0xab 0 64k
|
||||||
|
read -P0xad 0x00f8000 64k
|
||||||
|
read -P0x1d 0x2008000 64k
|
||||||
|
read -P0xea 0x3fe0000 64k
|
||||||
|
read -P0xd5 0x108000 32k
|
||||||
|
read -P0xdc 32M 32k
|
||||||
|
read -P0xcd 0x3ff0000 64k
|
||||||
|
|
||||||
|
Done
|
||||||
|
@ -301,6 +301,39 @@ bool hbitmap_next_dirty_area(const HBitmap *hb, int64_t start, int64_t end,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hbitmap_status(const HBitmap *hb, int64_t start, int64_t count,
|
||||||
|
int64_t *pnum)
|
||||||
|
{
|
||||||
|
int64_t next_dirty, next_zero;
|
||||||
|
|
||||||
|
assert(start >= 0);
|
||||||
|
assert(count > 0);
|
||||||
|
assert(start + count <= hb->orig_size);
|
||||||
|
|
||||||
|
next_dirty = hbitmap_next_dirty(hb, start, count);
|
||||||
|
if (next_dirty == -1) {
|
||||||
|
*pnum = count;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next_dirty > start) {
|
||||||
|
*pnum = next_dirty - start;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(next_dirty == start);
|
||||||
|
|
||||||
|
next_zero = hbitmap_next_zero(hb, start, count);
|
||||||
|
if (next_zero == -1) {
|
||||||
|
*pnum = count;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(next_zero > start);
|
||||||
|
*pnum = next_zero - start;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool hbitmap_empty(const HBitmap *hb)
|
bool hbitmap_empty(const HBitmap *hb)
|
||||||
{
|
{
|
||||||
return hb->count == 0;
|
return hb->count == 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user