mirror of
https://git.proxmox.com/git/qemu
synced 2025-08-09 06:36:08 +00:00
Merge remote-tracking branch 'stefanha/block' into staging
# By Paolo Bonzini (21) and others # Via Stefan Hajnoczi * stefanha/block: (42 commits) qemu-iotests: Fixed test case 026 qemu-iotests: Whitespace cleanup dataplane: Fix startup race. block: look for zero blocks in bs->file block: add default get_block_status implementation for protocols raw-posix: report unwritten extents as zero raw-posix: return get_block_status data and flags docs, qapi: document qemu-img map qemu-img: add a "map" subcommand block: return BDRV_BLOCK_ZERO past end of backing file block: use bdrv_has_zero_init to return BDRV_BLOCK_ZERO block: return get_block_status data and flags for formats block: define get_block_status return value block: introduce bdrv_get_block_status API block: make bdrv_has_zero_init return false for copy-on-write-images qemu-img: always probe the input image for allocated sectors block: expect errors from bdrv_co_is_allocated block: remove bdrv_is_allocated_above/bdrv_co_is_allocated_above distinction block: do not use ->total_sectors in bdrv_co_is_allocated block: make bdrv_co_is_allocated static ... Message-id: 1378481953-23099-1-git-send-email-stefanha@redhat.com Signed-off-by: Anthony Liguori <anthony@codemonkey.ws>
This commit is contained in:
commit
964737ea19
@ -18,6 +18,28 @@ Example:
|
|||||||
"data": { "actual": 944766976 },
|
"data": { "actual": 944766976 },
|
||||||
"timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
|
"timestamp": { "seconds": 1267020223, "microseconds": 435656 } }
|
||||||
|
|
||||||
|
BLOCK_IMAGE_CORRUPTED
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Emitted when a disk image is being marked corrupt.
|
||||||
|
|
||||||
|
Data:
|
||||||
|
|
||||||
|
- "device": Device name (json-string)
|
||||||
|
- "msg": Informative message (e.g., reason for the corruption) (json-string)
|
||||||
|
- "offset": If the corruption resulted from an image access, this is the access
|
||||||
|
offset into the image (json-int)
|
||||||
|
- "size": If the corruption resulted from an image access, this is the access
|
||||||
|
size (json-int)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
{ "event": "BLOCK_IMAGE_CORRUPTED",
|
||||||
|
"data": { "device": "ide0-hd0",
|
||||||
|
"msg": "Prevented active L1 table overwrite", "offset": 196608,
|
||||||
|
"size": 65536 },
|
||||||
|
"timestamp": { "seconds": 1378126126, "microseconds": 966463 } }
|
||||||
|
|
||||||
BLOCK_IO_ERROR
|
BLOCK_IO_ERROR
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
@ -336,8 +336,8 @@ static void init_blk_migration_it(void *opaque, BlockDriverState *bs)
|
|||||||
bmds->completed_sectors = 0;
|
bmds->completed_sectors = 0;
|
||||||
bmds->shared_base = block_mig_state.shared_base;
|
bmds->shared_base = block_mig_state.shared_base;
|
||||||
alloc_aio_bitmap(bmds);
|
alloc_aio_bitmap(bmds);
|
||||||
drive_get_ref(drive_get_by_blockdev(bs));
|
|
||||||
bdrv_set_in_use(bs, 1);
|
bdrv_set_in_use(bs, 1);
|
||||||
|
bdrv_ref(bs);
|
||||||
|
|
||||||
block_mig_state.total_sector_sum += sectors;
|
block_mig_state.total_sector_sum += sectors;
|
||||||
|
|
||||||
@ -575,7 +575,7 @@ static void blk_mig_cleanup(void)
|
|||||||
while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
|
while ((bmds = QSIMPLEQ_FIRST(&block_mig_state.bmds_list)) != NULL) {
|
||||||
QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
|
QSIMPLEQ_REMOVE_HEAD(&block_mig_state.bmds_list, entry);
|
||||||
bdrv_set_in_use(bmds->bs, 0);
|
bdrv_set_in_use(bmds->bs, 0);
|
||||||
drive_put_ref(drive_get_by_blockdev(bmds->bs));
|
bdrv_unref(bmds->bs);
|
||||||
g_free(bmds->aio_bitmap);
|
g_free(bmds->aio_bitmap);
|
||||||
g_free(bmds);
|
g_free(bmds);
|
||||||
}
|
}
|
||||||
|
558
block.c
558
block.c
@ -86,13 +86,6 @@ static void coroutine_fn bdrv_co_do_rw(void *opaque);
|
|||||||
static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs,
|
static int coroutine_fn bdrv_co_do_write_zeroes(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors);
|
int64_t sector_num, int nb_sectors);
|
||||||
|
|
||||||
static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors,
|
|
||||||
bool is_write, double elapsed_time, uint64_t *wait);
|
|
||||||
static bool bdrv_exceed_iops_limits(BlockDriverState *bs, bool is_write,
|
|
||||||
double elapsed_time, uint64_t *wait);
|
|
||||||
static bool bdrv_exceed_io_limits(BlockDriverState *bs, int nb_sectors,
|
|
||||||
bool is_write, int64_t *wait);
|
|
||||||
|
|
||||||
static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
|
static QTAILQ_HEAD(, BlockDriverState) bdrv_states =
|
||||||
QTAILQ_HEAD_INITIALIZER(bdrv_states);
|
QTAILQ_HEAD_INITIALIZER(bdrv_states);
|
||||||
|
|
||||||
@ -123,69 +116,101 @@ int is_windows_drive(const char *filename)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* throttling disk I/O limits */
|
/* throttling disk I/O limits */
|
||||||
|
void bdrv_set_io_limits(BlockDriverState *bs,
|
||||||
|
ThrottleConfig *cfg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
throttle_config(&bs->throttle_state, cfg);
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
qemu_co_enter_next(&bs->throttled_reqs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this function drain all the throttled IOs */
|
||||||
|
static bool bdrv_start_throttled_reqs(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
bool drained = false;
|
||||||
|
bool enabled = bs->io_limits_enabled;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
bs->io_limits_enabled = false;
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
while (qemu_co_enter_next(&bs->throttled_reqs[i])) {
|
||||||
|
drained = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bs->io_limits_enabled = enabled;
|
||||||
|
|
||||||
|
return drained;
|
||||||
|
}
|
||||||
|
|
||||||
void bdrv_io_limits_disable(BlockDriverState *bs)
|
void bdrv_io_limits_disable(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
bs->io_limits_enabled = false;
|
bs->io_limits_enabled = false;
|
||||||
|
|
||||||
do {} while (qemu_co_enter_next(&bs->throttled_reqs));
|
bdrv_start_throttled_reqs(bs);
|
||||||
|
|
||||||
if (bs->block_timer) {
|
throttle_destroy(&bs->throttle_state);
|
||||||
timer_del(bs->block_timer);
|
|
||||||
timer_free(bs->block_timer);
|
|
||||||
bs->block_timer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bs->slice_start = 0;
|
|
||||||
bs->slice_end = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bdrv_block_timer(void *opaque)
|
static void bdrv_throttle_read_timer_cb(void *opaque)
|
||||||
{
|
{
|
||||||
BlockDriverState *bs = opaque;
|
BlockDriverState *bs = opaque;
|
||||||
|
qemu_co_enter_next(&bs->throttled_reqs[0]);
|
||||||
qemu_co_enter_next(&bs->throttled_reqs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void bdrv_throttle_write_timer_cb(void *opaque)
|
||||||
|
{
|
||||||
|
BlockDriverState *bs = opaque;
|
||||||
|
qemu_co_enter_next(&bs->throttled_reqs[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* should be called before bdrv_set_io_limits if a limit is set */
|
||||||
void bdrv_io_limits_enable(BlockDriverState *bs)
|
void bdrv_io_limits_enable(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
bs->block_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, bdrv_block_timer, bs);
|
assert(!bs->io_limits_enabled);
|
||||||
|
throttle_init(&bs->throttle_state,
|
||||||
|
QEMU_CLOCK_VIRTUAL,
|
||||||
|
bdrv_throttle_read_timer_cb,
|
||||||
|
bdrv_throttle_write_timer_cb,
|
||||||
|
bs);
|
||||||
bs->io_limits_enabled = true;
|
bs->io_limits_enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bdrv_io_limits_enabled(BlockDriverState *bs)
|
/* This function makes an IO wait if needed
|
||||||
{
|
*
|
||||||
BlockIOLimit *io_limits = &bs->io_limits;
|
* @nb_sectors: the number of sectors of the IO
|
||||||
return io_limits->bps[BLOCK_IO_LIMIT_READ]
|
* @is_write: is the IO a write
|
||||||
|| io_limits->bps[BLOCK_IO_LIMIT_WRITE]
|
*/
|
||||||
|| io_limits->bps[BLOCK_IO_LIMIT_TOTAL]
|
|
||||||
|| io_limits->iops[BLOCK_IO_LIMIT_READ]
|
|
||||||
|| io_limits->iops[BLOCK_IO_LIMIT_WRITE]
|
|
||||||
|| io_limits->iops[BLOCK_IO_LIMIT_TOTAL];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void bdrv_io_limits_intercept(BlockDriverState *bs,
|
static void bdrv_io_limits_intercept(BlockDriverState *bs,
|
||||||
bool is_write, int nb_sectors)
|
int nb_sectors,
|
||||||
|
bool is_write)
|
||||||
{
|
{
|
||||||
int64_t wait_time = -1;
|
/* does this io must wait */
|
||||||
|
bool must_wait = throttle_schedule_timer(&bs->throttle_state, is_write);
|
||||||
|
|
||||||
if (!qemu_co_queue_empty(&bs->throttled_reqs)) {
|
/* if must wait or any request of this type throttled queue the IO */
|
||||||
qemu_co_queue_wait(&bs->throttled_reqs);
|
if (must_wait ||
|
||||||
|
!qemu_co_queue_empty(&bs->throttled_reqs[is_write])) {
|
||||||
|
qemu_co_queue_wait(&bs->throttled_reqs[is_write]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In fact, we hope to keep each request's timing, in FIFO mode. The next
|
/* the IO will be executed, do the accounting */
|
||||||
* throttled requests will not be dequeued until the current request is
|
throttle_account(&bs->throttle_state,
|
||||||
* allowed to be serviced. So if the current request still exceeds the
|
is_write,
|
||||||
* limits, it will be inserted to the head. All requests followed it will
|
nb_sectors * BDRV_SECTOR_SIZE);
|
||||||
* be still in throttled_reqs queue.
|
|
||||||
*/
|
|
||||||
|
|
||||||
while (bdrv_exceed_io_limits(bs, nb_sectors, is_write, &wait_time)) {
|
/* if the next request must wait -> do nothing */
|
||||||
timer_mod(bs->block_timer,
|
if (throttle_schedule_timer(&bs->throttle_state, is_write)) {
|
||||||
wait_time + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
|
return;
|
||||||
qemu_co_queue_wait_insert_head(&bs->throttled_reqs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu_co_queue_next(&bs->throttled_reqs);
|
/* else queue next request for execution */
|
||||||
|
qemu_co_queue_next(&bs->throttled_reqs[is_write]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if the path starts with "<protocol>:" */
|
/* check if the path starts with "<protocol>:" */
|
||||||
@ -305,7 +330,9 @@ BlockDriverState *bdrv_new(const char *device_name)
|
|||||||
bdrv_iostatus_disable(bs);
|
bdrv_iostatus_disable(bs);
|
||||||
notifier_list_init(&bs->close_notifiers);
|
notifier_list_init(&bs->close_notifiers);
|
||||||
notifier_with_return_list_init(&bs->before_write_notifiers);
|
notifier_with_return_list_init(&bs->before_write_notifiers);
|
||||||
qemu_co_queue_init(&bs->throttled_reqs);
|
qemu_co_queue_init(&bs->throttled_reqs[0]);
|
||||||
|
qemu_co_queue_init(&bs->throttled_reqs[1]);
|
||||||
|
bs->refcnt = 1;
|
||||||
|
|
||||||
return bs;
|
return bs;
|
||||||
}
|
}
|
||||||
@ -876,7 +903,7 @@ fail:
|
|||||||
if (!bs->drv) {
|
if (!bs->drv) {
|
||||||
QDECREF(bs->options);
|
QDECREF(bs->options);
|
||||||
}
|
}
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -927,7 +954,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options)
|
|||||||
*backing_filename ? backing_filename : NULL, options,
|
*backing_filename ? backing_filename : NULL, options,
|
||||||
back_flags, back_drv);
|
back_flags, back_drv);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_delete(bs->backing_hd);
|
bdrv_unref(bs->backing_hd);
|
||||||
bs->backing_hd = NULL;
|
bs->backing_hd = NULL;
|
||||||
bs->open_flags |= BDRV_O_NO_BACKING;
|
bs->open_flags |= BDRV_O_NO_BACKING;
|
||||||
return ret;
|
return ret;
|
||||||
@ -1002,12 +1029,12 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
|||||||
bs1 = bdrv_new("");
|
bs1 = bdrv_new("");
|
||||||
ret = bdrv_open(bs1, filename, NULL, 0, drv);
|
ret = bdrv_open(bs1, filename, NULL, 0, drv);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_delete(bs1);
|
bdrv_unref(bs1);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK;
|
total_size = bdrv_getlength(bs1) & BDRV_SECTOR_MASK;
|
||||||
|
|
||||||
bdrv_delete(bs1);
|
bdrv_unref(bs1);
|
||||||
|
|
||||||
ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename));
|
ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -1081,7 +1108,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bs->file != file) {
|
if (bs->file != file) {
|
||||||
bdrv_delete(file);
|
bdrv_unref(file);
|
||||||
file = NULL;
|
file = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1112,16 +1139,11 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options,
|
|||||||
bdrv_dev_change_media_cb(bs, true);
|
bdrv_dev_change_media_cb(bs, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* throttling disk I/O limits */
|
|
||||||
if (bs->io_limits_enabled) {
|
|
||||||
bdrv_io_limits_enable(bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
unlink_and_fail:
|
unlink_and_fail:
|
||||||
if (file != NULL) {
|
if (file != NULL) {
|
||||||
bdrv_delete(file);
|
bdrv_unref(file);
|
||||||
}
|
}
|
||||||
if (bs->is_temporary) {
|
if (bs->is_temporary) {
|
||||||
unlink(filename);
|
unlink(filename);
|
||||||
@ -1382,7 +1404,7 @@ void bdrv_close(BlockDriverState *bs)
|
|||||||
|
|
||||||
if (bs->drv) {
|
if (bs->drv) {
|
||||||
if (bs->backing_hd) {
|
if (bs->backing_hd) {
|
||||||
bdrv_delete(bs->backing_hd);
|
bdrv_unref(bs->backing_hd);
|
||||||
bs->backing_hd = NULL;
|
bs->backing_hd = NULL;
|
||||||
}
|
}
|
||||||
bs->drv->bdrv_close(bs);
|
bs->drv->bdrv_close(bs);
|
||||||
@ -1407,7 +1429,7 @@ void bdrv_close(BlockDriverState *bs)
|
|||||||
bs->options = NULL;
|
bs->options = NULL;
|
||||||
|
|
||||||
if (bs->file != NULL) {
|
if (bs->file != NULL) {
|
||||||
bdrv_delete(bs->file);
|
bdrv_unref(bs->file);
|
||||||
bs->file = NULL;
|
bs->file = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1435,7 +1457,10 @@ static bool bdrv_requests_pending(BlockDriverState *bs)
|
|||||||
if (!QLIST_EMPTY(&bs->tracked_requests)) {
|
if (!QLIST_EMPTY(&bs->tracked_requests)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!qemu_co_queue_empty(&bs->throttled_reqs)) {
|
if (!qemu_co_queue_empty(&bs->throttled_reqs[0])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!qemu_co_queue_empty(&bs->throttled_reqs[1])) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (bs->file && bdrv_requests_pending(bs->file)) {
|
if (bs->file && bdrv_requests_pending(bs->file)) {
|
||||||
@ -1481,7 +1506,7 @@ void bdrv_drain_all(void)
|
|||||||
* a busy wait.
|
* a busy wait.
|
||||||
*/
|
*/
|
||||||
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
QTAILQ_FOREACH(bs, &bdrv_states, list) {
|
||||||
while (qemu_co_enter_next(&bs->throttled_reqs)) {
|
if (bdrv_start_throttled_reqs(bs)) {
|
||||||
busy = true;
|
busy = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1523,13 +1548,12 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
|
|||||||
|
|
||||||
bs_dest->enable_write_cache = bs_src->enable_write_cache;
|
bs_dest->enable_write_cache = bs_src->enable_write_cache;
|
||||||
|
|
||||||
/* i/o timing parameters */
|
/* i/o throttled req */
|
||||||
bs_dest->slice_start = bs_src->slice_start;
|
memcpy(&bs_dest->throttle_state,
|
||||||
bs_dest->slice_end = bs_src->slice_end;
|
&bs_src->throttle_state,
|
||||||
bs_dest->slice_submitted = bs_src->slice_submitted;
|
sizeof(ThrottleState));
|
||||||
bs_dest->io_limits = bs_src->io_limits;
|
bs_dest->throttled_reqs[0] = bs_src->throttled_reqs[0];
|
||||||
bs_dest->throttled_reqs = bs_src->throttled_reqs;
|
bs_dest->throttled_reqs[1] = bs_src->throttled_reqs[1];
|
||||||
bs_dest->block_timer = bs_src->block_timer;
|
|
||||||
bs_dest->io_limits_enabled = bs_src->io_limits_enabled;
|
bs_dest->io_limits_enabled = bs_src->io_limits_enabled;
|
||||||
|
|
||||||
/* r/w error */
|
/* r/w error */
|
||||||
@ -1543,6 +1567,9 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest,
|
|||||||
/* dirty bitmap */
|
/* dirty bitmap */
|
||||||
bs_dest->dirty_bitmap = bs_src->dirty_bitmap;
|
bs_dest->dirty_bitmap = bs_src->dirty_bitmap;
|
||||||
|
|
||||||
|
/* reference count */
|
||||||
|
bs_dest->refcnt = bs_src->refcnt;
|
||||||
|
|
||||||
/* job */
|
/* job */
|
||||||
bs_dest->in_use = bs_src->in_use;
|
bs_dest->in_use = bs_src->in_use;
|
||||||
bs_dest->job = bs_src->job;
|
bs_dest->job = bs_src->job;
|
||||||
@ -1576,7 +1603,7 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
|
|||||||
assert(bs_new->dev == NULL);
|
assert(bs_new->dev == NULL);
|
||||||
assert(bs_new->in_use == 0);
|
assert(bs_new->in_use == 0);
|
||||||
assert(bs_new->io_limits_enabled == false);
|
assert(bs_new->io_limits_enabled == false);
|
||||||
assert(bs_new->block_timer == NULL);
|
assert(!throttle_have_timer(&bs_new->throttle_state));
|
||||||
|
|
||||||
tmp = *bs_new;
|
tmp = *bs_new;
|
||||||
*bs_new = *bs_old;
|
*bs_new = *bs_old;
|
||||||
@ -1595,7 +1622,7 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old)
|
|||||||
assert(bs_new->job == NULL);
|
assert(bs_new->job == NULL);
|
||||||
assert(bs_new->in_use == 0);
|
assert(bs_new->in_use == 0);
|
||||||
assert(bs_new->io_limits_enabled == false);
|
assert(bs_new->io_limits_enabled == false);
|
||||||
assert(bs_new->block_timer == NULL);
|
assert(!throttle_have_timer(&bs_new->throttle_state));
|
||||||
|
|
||||||
bdrv_rebind(bs_new);
|
bdrv_rebind(bs_new);
|
||||||
bdrv_rebind(bs_old);
|
bdrv_rebind(bs_old);
|
||||||
@ -1626,11 +1653,12 @@ void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top)
|
|||||||
bs_new->drv ? bs_new->drv->format_name : "");
|
bs_new->drv ? bs_new->drv->format_name : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void bdrv_delete(BlockDriverState *bs)
|
static void bdrv_delete(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
assert(!bs->dev);
|
assert(!bs->dev);
|
||||||
assert(!bs->job);
|
assert(!bs->job);
|
||||||
assert(!bs->in_use);
|
assert(!bs->in_use);
|
||||||
|
assert(!bs->refcnt);
|
||||||
|
|
||||||
bdrv_close(bs);
|
bdrv_close(bs);
|
||||||
|
|
||||||
@ -1829,8 +1857,11 @@ int bdrv_commit(BlockDriverState *bs)
|
|||||||
buf = g_malloc(COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE);
|
buf = g_malloc(COMMIT_BUF_SECTORS * BDRV_SECTOR_SIZE);
|
||||||
|
|
||||||
for (sector = 0; sector < total_sectors; sector += n) {
|
for (sector = 0; sector < total_sectors; sector += n) {
|
||||||
if (bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n)) {
|
ret = bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto ro_cleanup;
|
||||||
|
}
|
||||||
|
if (ret) {
|
||||||
if (bdrv_read(bs, sector, buf, n) != 0) {
|
if (bdrv_read(bs, sector, buf, n) != 0) {
|
||||||
ret = -EIO;
|
ret = -EIO;
|
||||||
goto ro_cleanup;
|
goto ro_cleanup;
|
||||||
@ -2146,7 +2177,7 @@ int bdrv_drop_intermediate(BlockDriverState *active, BlockDriverState *top,
|
|||||||
QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) {
|
QSIMPLEQ_FOREACH_SAFE(intermediate_state, &states_to_delete, entry, next) {
|
||||||
/* so that bdrv_close() does not recursively close the chain */
|
/* so that bdrv_close() does not recursively close the chain */
|
||||||
intermediate_state->bs->backing_hd = NULL;
|
intermediate_state->bs->backing_hd = NULL;
|
||||||
bdrv_delete(intermediate_state->bs);
|
bdrv_unref(intermediate_state->bs);
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
@ -2538,11 +2569,6 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs,
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* throttling disk read I/O */
|
|
||||||
if (bs->io_limits_enabled) {
|
|
||||||
bdrv_io_limits_intercept(bs, false, nb_sectors);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bs->copy_on_read) {
|
if (bs->copy_on_read) {
|
||||||
flags |= BDRV_REQ_COPY_ON_READ;
|
flags |= BDRV_REQ_COPY_ON_READ;
|
||||||
}
|
}
|
||||||
@ -2554,12 +2580,17 @@ static int coroutine_fn bdrv_co_do_readv(BlockDriverState *bs,
|
|||||||
wait_for_overlapping_requests(bs, sector_num, nb_sectors);
|
wait_for_overlapping_requests(bs, sector_num, nb_sectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* throttling disk I/O */
|
||||||
|
if (bs->io_limits_enabled) {
|
||||||
|
bdrv_io_limits_intercept(bs, nb_sectors, false);
|
||||||
|
}
|
||||||
|
|
||||||
tracked_request_begin(&req, bs, sector_num, nb_sectors, false);
|
tracked_request_begin(&req, bs, sector_num, nb_sectors, false);
|
||||||
|
|
||||||
if (flags & BDRV_REQ_COPY_ON_READ) {
|
if (flags & BDRV_REQ_COPY_ON_READ) {
|
||||||
int pnum;
|
int pnum;
|
||||||
|
|
||||||
ret = bdrv_co_is_allocated(bs, sector_num, nb_sectors, &pnum);
|
ret = bdrv_is_allocated(bs, sector_num, nb_sectors, &pnum);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -2679,15 +2710,15 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* throttling disk write I/O */
|
|
||||||
if (bs->io_limits_enabled) {
|
|
||||||
bdrv_io_limits_intercept(bs, true, nb_sectors);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bs->copy_on_read_in_flight) {
|
if (bs->copy_on_read_in_flight) {
|
||||||
wait_for_overlapping_requests(bs, sector_num, nb_sectors);
|
wait_for_overlapping_requests(bs, sector_num, nb_sectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* throttling disk I/O */
|
||||||
|
if (bs->io_limits_enabled) {
|
||||||
|
bdrv_io_limits_intercept(bs, nb_sectors, true);
|
||||||
|
}
|
||||||
|
|
||||||
tracked_request_begin(&req, bs, sector_num, nb_sectors, true);
|
tracked_request_begin(&req, bs, sector_num, nb_sectors, true);
|
||||||
|
|
||||||
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, &req);
|
ret = notifier_with_return_list_notify(&bs->before_write_notifiers, &req);
|
||||||
@ -2711,6 +2742,9 @@ static int coroutine_fn bdrv_co_do_writev(BlockDriverState *bs,
|
|||||||
if (bs->wr_highest_sector < sector_num + nb_sectors - 1) {
|
if (bs->wr_highest_sector < sector_num + nb_sectors - 1) {
|
||||||
bs->wr_highest_sector = sector_num + nb_sectors - 1;
|
bs->wr_highest_sector = sector_num + nb_sectors - 1;
|
||||||
}
|
}
|
||||||
|
if (bs->growable && ret >= 0) {
|
||||||
|
bs->total_sectors = MAX(bs->total_sectors, sector_num + nb_sectors);
|
||||||
|
}
|
||||||
|
|
||||||
tracked_request_end(&req);
|
tracked_request_end(&req);
|
||||||
|
|
||||||
@ -2785,7 +2819,7 @@ int64_t bdrv_getlength(BlockDriverState *bs)
|
|||||||
if (!drv)
|
if (!drv)
|
||||||
return -ENOMEDIUM;
|
return -ENOMEDIUM;
|
||||||
|
|
||||||
if (bs->growable || bdrv_dev_has_removable_media(bs)) {
|
if (bdrv_dev_has_removable_media(bs)) {
|
||||||
if (drv->bdrv_getlength) {
|
if (drv->bdrv_getlength) {
|
||||||
return drv->bdrv_getlength(bs);
|
return drv->bdrv_getlength(bs);
|
||||||
}
|
}
|
||||||
@ -2805,14 +2839,6 @@ void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr)
|
|||||||
*nb_sectors_ptr = length;
|
*nb_sectors_ptr = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* throttling disk io limits */
|
|
||||||
void bdrv_set_io_limits(BlockDriverState *bs,
|
|
||||||
BlockIOLimit *io_limits)
|
|
||||||
{
|
|
||||||
bs->io_limits = *io_limits;
|
|
||||||
bs->io_limits_enabled = bdrv_io_limits_enabled(bs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error,
|
void bdrv_set_on_error(BlockDriverState *bs, BlockdevOnError on_read_error,
|
||||||
BlockdevOnError on_write_error)
|
BlockdevOnError on_write_error)
|
||||||
{
|
{
|
||||||
@ -3005,6 +3031,11 @@ int bdrv_has_zero_init(BlockDriverState *bs)
|
|||||||
{
|
{
|
||||||
assert(bs->drv);
|
assert(bs->drv);
|
||||||
|
|
||||||
|
/* If BS is a copy on write image, it is initialized to
|
||||||
|
the contents of the base image, which may not be zeroes. */
|
||||||
|
if (bs->backing_hd) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (bs->drv->bdrv_has_zero_init) {
|
if (bs->drv->bdrv_has_zero_init) {
|
||||||
return bs->drv->bdrv_has_zero_init(bs);
|
return bs->drv->bdrv_has_zero_init(bs);
|
||||||
}
|
}
|
||||||
@ -3013,15 +3044,15 @@ int bdrv_has_zero_init(BlockDriverState *bs)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct BdrvCoIsAllocatedData {
|
typedef struct BdrvCoGetBlockStatusData {
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
BlockDriverState *base;
|
BlockDriverState *base;
|
||||||
int64_t sector_num;
|
int64_t sector_num;
|
||||||
int nb_sectors;
|
int nb_sectors;
|
||||||
int *pnum;
|
int *pnum;
|
||||||
int ret;
|
int64_t ret;
|
||||||
bool done;
|
bool done;
|
||||||
} BdrvCoIsAllocatedData;
|
} BdrvCoGetBlockStatusData;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns true iff the specified sector is present in the disk image. Drivers
|
* Returns true iff the specified sector is present in the disk image. Drivers
|
||||||
@ -3038,12 +3069,20 @@ typedef struct BdrvCoIsAllocatedData {
|
|||||||
* 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes
|
* 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes
|
||||||
* beyond the end of the disk image it will be clamped.
|
* beyond the end of the disk image it will be clamped.
|
||||||
*/
|
*/
|
||||||
int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
static int64_t coroutine_fn bdrv_co_get_block_status(BlockDriverState *bs,
|
||||||
int nb_sectors, int *pnum)
|
int64_t sector_num,
|
||||||
|
int nb_sectors, int *pnum)
|
||||||
{
|
{
|
||||||
|
int64_t length;
|
||||||
int64_t n;
|
int64_t n;
|
||||||
|
int64_t ret, ret2;
|
||||||
|
|
||||||
if (sector_num >= bs->total_sectors) {
|
length = bdrv_getlength(bs);
|
||||||
|
if (length < 0) {
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sector_num >= (length >> BDRV_SECTOR_BITS)) {
|
||||||
*pnum = 0;
|
*pnum = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -3053,35 +3092,69 @@ int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
|||||||
nb_sectors = n;
|
nb_sectors = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bs->drv->bdrv_co_is_allocated) {
|
if (!bs->drv->bdrv_co_get_block_status) {
|
||||||
*pnum = nb_sectors;
|
*pnum = nb_sectors;
|
||||||
return 1;
|
ret = BDRV_BLOCK_DATA;
|
||||||
|
if (bs->drv->protocol_name) {
|
||||||
|
ret |= BDRV_BLOCK_OFFSET_VALID | (sector_num * BDRV_SECTOR_SIZE);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return bs->drv->bdrv_co_is_allocated(bs, sector_num, nb_sectors, pnum);
|
ret = bs->drv->bdrv_co_get_block_status(bs, sector_num, nb_sectors, pnum);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(ret & BDRV_BLOCK_DATA)) {
|
||||||
|
if (bdrv_has_zero_init(bs)) {
|
||||||
|
ret |= BDRV_BLOCK_ZERO;
|
||||||
|
} else {
|
||||||
|
BlockDriverState *bs2 = bs->backing_hd;
|
||||||
|
int64_t length2 = bdrv_getlength(bs2);
|
||||||
|
if (length2 >= 0 && sector_num >= (length2 >> BDRV_SECTOR_BITS)) {
|
||||||
|
ret |= BDRV_BLOCK_ZERO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bs->file &&
|
||||||
|
(ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) &&
|
||||||
|
(ret & BDRV_BLOCK_OFFSET_VALID)) {
|
||||||
|
ret2 = bdrv_co_get_block_status(bs->file, ret >> BDRV_SECTOR_BITS,
|
||||||
|
*pnum, pnum);
|
||||||
|
if (ret2 >= 0) {
|
||||||
|
/* Ignore errors. This is just providing extra information, it
|
||||||
|
* is useful but not necessary.
|
||||||
|
*/
|
||||||
|
ret |= (ret2 & BDRV_BLOCK_ZERO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Coroutine wrapper for bdrv_is_allocated() */
|
/* Coroutine wrapper for bdrv_get_block_status() */
|
||||||
static void coroutine_fn bdrv_is_allocated_co_entry(void *opaque)
|
static void coroutine_fn bdrv_get_block_status_co_entry(void *opaque)
|
||||||
{
|
{
|
||||||
BdrvCoIsAllocatedData *data = opaque;
|
BdrvCoGetBlockStatusData *data = opaque;
|
||||||
BlockDriverState *bs = data->bs;
|
BlockDriverState *bs = data->bs;
|
||||||
|
|
||||||
data->ret = bdrv_co_is_allocated(bs, data->sector_num, data->nb_sectors,
|
data->ret = bdrv_co_get_block_status(bs, data->sector_num, data->nb_sectors,
|
||||||
data->pnum);
|
data->pnum);
|
||||||
data->done = true;
|
data->done = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Synchronous wrapper around bdrv_co_is_allocated().
|
* Synchronous wrapper around bdrv_co_get_block_status().
|
||||||
*
|
*
|
||||||
* See bdrv_co_is_allocated() for details.
|
* See bdrv_co_get_block_status() for details.
|
||||||
*/
|
*/
|
||||||
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num,
|
||||||
int *pnum)
|
int nb_sectors, int *pnum)
|
||||||
{
|
{
|
||||||
Coroutine *co;
|
Coroutine *co;
|
||||||
BdrvCoIsAllocatedData data = {
|
BdrvCoGetBlockStatusData data = {
|
||||||
.bs = bs,
|
.bs = bs,
|
||||||
.sector_num = sector_num,
|
.sector_num = sector_num,
|
||||||
.nb_sectors = nb_sectors,
|
.nb_sectors = nb_sectors,
|
||||||
@ -3089,14 +3162,31 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
|||||||
.done = false,
|
.done = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
co = qemu_coroutine_create(bdrv_is_allocated_co_entry);
|
if (qemu_in_coroutine()) {
|
||||||
qemu_coroutine_enter(co, &data);
|
/* Fast-path if already in coroutine context */
|
||||||
while (!data.done) {
|
bdrv_get_block_status_co_entry(&data);
|
||||||
qemu_aio_wait();
|
} else {
|
||||||
|
co = qemu_coroutine_create(bdrv_get_block_status_co_entry);
|
||||||
|
qemu_coroutine_enter(co, &data);
|
||||||
|
while (!data.done) {
|
||||||
|
qemu_aio_wait();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return data.ret;
|
return data.ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int coroutine_fn bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
||||||
|
int nb_sectors, int *pnum)
|
||||||
|
{
|
||||||
|
int64_t ret = bdrv_get_block_status(bs, sector_num, nb_sectors, pnum);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return
|
||||||
|
(ret & BDRV_BLOCK_DATA) ||
|
||||||
|
((ret & BDRV_BLOCK_ZERO) && !bdrv_has_zero_init(bs));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP]
|
* Given an image chain: ... -> [BASE] -> [INTER1] -> [INTER2] -> [TOP]
|
||||||
*
|
*
|
||||||
@ -3109,10 +3199,10 @@ int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
|||||||
* allocated/unallocated state.
|
* allocated/unallocated state.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
|
int bdrv_is_allocated_above(BlockDriverState *top,
|
||||||
BlockDriverState *base,
|
BlockDriverState *base,
|
||||||
int64_t sector_num,
|
int64_t sector_num,
|
||||||
int nb_sectors, int *pnum)
|
int nb_sectors, int *pnum)
|
||||||
{
|
{
|
||||||
BlockDriverState *intermediate;
|
BlockDriverState *intermediate;
|
||||||
int ret, n = nb_sectors;
|
int ret, n = nb_sectors;
|
||||||
@ -3120,8 +3210,8 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
|
|||||||
intermediate = top;
|
intermediate = top;
|
||||||
while (intermediate && intermediate != base) {
|
while (intermediate && intermediate != base) {
|
||||||
int pnum_inter;
|
int pnum_inter;
|
||||||
ret = bdrv_co_is_allocated(intermediate, sector_num, nb_sectors,
|
ret = bdrv_is_allocated(intermediate, sector_num, nb_sectors,
|
||||||
&pnum_inter);
|
&pnum_inter);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
} else if (ret) {
|
} else if (ret) {
|
||||||
@ -3148,44 +3238,6 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Coroutine wrapper for bdrv_is_allocated_above() */
|
|
||||||
static void coroutine_fn bdrv_is_allocated_above_co_entry(void *opaque)
|
|
||||||
{
|
|
||||||
BdrvCoIsAllocatedData *data = opaque;
|
|
||||||
BlockDriverState *top = data->bs;
|
|
||||||
BlockDriverState *base = data->base;
|
|
||||||
|
|
||||||
data->ret = bdrv_co_is_allocated_above(top, base, data->sector_num,
|
|
||||||
data->nb_sectors, data->pnum);
|
|
||||||
data->done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Synchronous wrapper around bdrv_co_is_allocated_above().
|
|
||||||
*
|
|
||||||
* See bdrv_co_is_allocated_above() for details.
|
|
||||||
*/
|
|
||||||
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
|
|
||||||
int64_t sector_num, int nb_sectors, int *pnum)
|
|
||||||
{
|
|
||||||
Coroutine *co;
|
|
||||||
BdrvCoIsAllocatedData data = {
|
|
||||||
.bs = top,
|
|
||||||
.base = base,
|
|
||||||
.sector_num = sector_num,
|
|
||||||
.nb_sectors = nb_sectors,
|
|
||||||
.pnum = pnum,
|
|
||||||
.done = false,
|
|
||||||
};
|
|
||||||
|
|
||||||
co = qemu_coroutine_create(bdrv_is_allocated_above_co_entry);
|
|
||||||
qemu_coroutine_enter(co, &data);
|
|
||||||
while (!data.done) {
|
|
||||||
qemu_aio_wait();
|
|
||||||
}
|
|
||||||
return data.ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *bdrv_get_encrypted_filename(BlockDriverState *bs)
|
const char *bdrv_get_encrypted_filename(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
if (bs->backing_hd && bs->backing_hd->encrypted)
|
if (bs->backing_hd && bs->backing_hd->encrypted)
|
||||||
@ -3622,169 +3674,6 @@ void bdrv_aio_cancel(BlockDriverAIOCB *acb)
|
|||||||
acb->aiocb_info->cancel(acb);
|
acb->aiocb_info->cancel(acb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* block I/O throttling */
|
|
||||||
static bool bdrv_exceed_bps_limits(BlockDriverState *bs, int nb_sectors,
|
|
||||||
bool is_write, double elapsed_time, uint64_t *wait)
|
|
||||||
{
|
|
||||||
uint64_t bps_limit = 0;
|
|
||||||
uint64_t extension;
|
|
||||||
double bytes_limit, bytes_base, bytes_res;
|
|
||||||
double slice_time, wait_time;
|
|
||||||
|
|
||||||
if (bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]) {
|
|
||||||
bps_limit = bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL];
|
|
||||||
} else if (bs->io_limits.bps[is_write]) {
|
|
||||||
bps_limit = bs->io_limits.bps[is_write];
|
|
||||||
} else {
|
|
||||||
if (wait) {
|
|
||||||
*wait = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
slice_time = bs->slice_end - bs->slice_start;
|
|
||||||
slice_time /= (NANOSECONDS_PER_SECOND);
|
|
||||||
bytes_limit = bps_limit * slice_time;
|
|
||||||
bytes_base = bs->slice_submitted.bytes[is_write];
|
|
||||||
if (bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL]) {
|
|
||||||
bytes_base += bs->slice_submitted.bytes[!is_write];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* bytes_base: the bytes of data which have been read/written; and
|
|
||||||
* it is obtained from the history statistic info.
|
|
||||||
* bytes_res: the remaining bytes of data which need to be read/written.
|
|
||||||
* (bytes_base + bytes_res) / bps_limit: used to calcuate
|
|
||||||
* the total time for completing reading/writting all data.
|
|
||||||
*/
|
|
||||||
bytes_res = (unsigned) nb_sectors * BDRV_SECTOR_SIZE;
|
|
||||||
|
|
||||||
if (bytes_base + bytes_res <= bytes_limit) {
|
|
||||||
if (wait) {
|
|
||||||
*wait = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calc approx time to dispatch */
|
|
||||||
wait_time = (bytes_base + bytes_res) / bps_limit - elapsed_time;
|
|
||||||
|
|
||||||
/* When the I/O rate at runtime exceeds the limits,
|
|
||||||
* bs->slice_end need to be extended in order that the current statistic
|
|
||||||
* info can be kept until the timer fire, so it is increased and tuned
|
|
||||||
* based on the result of experiment.
|
|
||||||
*/
|
|
||||||
extension = wait_time * NANOSECONDS_PER_SECOND;
|
|
||||||
extension = DIV_ROUND_UP(extension, BLOCK_IO_SLICE_TIME) *
|
|
||||||
BLOCK_IO_SLICE_TIME;
|
|
||||||
bs->slice_end += extension;
|
|
||||||
if (wait) {
|
|
||||||
*wait = wait_time * NANOSECONDS_PER_SECOND;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool bdrv_exceed_iops_limits(BlockDriverState *bs, bool is_write,
|
|
||||||
double elapsed_time, uint64_t *wait)
|
|
||||||
{
|
|
||||||
uint64_t iops_limit = 0;
|
|
||||||
double ios_limit, ios_base;
|
|
||||||
double slice_time, wait_time;
|
|
||||||
|
|
||||||
if (bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]) {
|
|
||||||
iops_limit = bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL];
|
|
||||||
} else if (bs->io_limits.iops[is_write]) {
|
|
||||||
iops_limit = bs->io_limits.iops[is_write];
|
|
||||||
} else {
|
|
||||||
if (wait) {
|
|
||||||
*wait = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
slice_time = bs->slice_end - bs->slice_start;
|
|
||||||
slice_time /= (NANOSECONDS_PER_SECOND);
|
|
||||||
ios_limit = iops_limit * slice_time;
|
|
||||||
ios_base = bs->slice_submitted.ios[is_write];
|
|
||||||
if (bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL]) {
|
|
||||||
ios_base += bs->slice_submitted.ios[!is_write];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ios_base + 1 <= ios_limit) {
|
|
||||||
if (wait) {
|
|
||||||
*wait = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calc approx time to dispatch, in seconds */
|
|
||||||
wait_time = (ios_base + 1) / iops_limit;
|
|
||||||
if (wait_time > elapsed_time) {
|
|
||||||
wait_time = wait_time - elapsed_time;
|
|
||||||
} else {
|
|
||||||
wait_time = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Exceeded current slice, extend it by another slice time */
|
|
||||||
bs->slice_end += BLOCK_IO_SLICE_TIME;
|
|
||||||
if (wait) {
|
|
||||||
*wait = wait_time * NANOSECONDS_PER_SECOND;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool bdrv_exceed_io_limits(BlockDriverState *bs, int nb_sectors,
|
|
||||||
bool is_write, int64_t *wait)
|
|
||||||
{
|
|
||||||
int64_t now, max_wait;
|
|
||||||
uint64_t bps_wait = 0, iops_wait = 0;
|
|
||||||
double elapsed_time;
|
|
||||||
int bps_ret, iops_ret;
|
|
||||||
|
|
||||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
|
||||||
if (now > bs->slice_end) {
|
|
||||||
bs->slice_start = now;
|
|
||||||
bs->slice_end = now + BLOCK_IO_SLICE_TIME;
|
|
||||||
memset(&bs->slice_submitted, 0, sizeof(bs->slice_submitted));
|
|
||||||
}
|
|
||||||
|
|
||||||
elapsed_time = now - bs->slice_start;
|
|
||||||
elapsed_time /= (NANOSECONDS_PER_SECOND);
|
|
||||||
|
|
||||||
bps_ret = bdrv_exceed_bps_limits(bs, nb_sectors,
|
|
||||||
is_write, elapsed_time, &bps_wait);
|
|
||||||
iops_ret = bdrv_exceed_iops_limits(bs, is_write,
|
|
||||||
elapsed_time, &iops_wait);
|
|
||||||
if (bps_ret || iops_ret) {
|
|
||||||
max_wait = bps_wait > iops_wait ? bps_wait : iops_wait;
|
|
||||||
if (wait) {
|
|
||||||
*wait = max_wait;
|
|
||||||
}
|
|
||||||
|
|
||||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
|
||||||
if (bs->slice_end < now + max_wait) {
|
|
||||||
bs->slice_end = now + max_wait;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wait) {
|
|
||||||
*wait = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bs->slice_submitted.bytes[is_write] += (int64_t)nb_sectors *
|
|
||||||
BDRV_SECTOR_SIZE;
|
|
||||||
bs->slice_submitted.ios[is_write]++;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**************************************************************/
|
/**************************************************************/
|
||||||
/* async block device emulation */
|
/* async block device emulation */
|
||||||
|
|
||||||
@ -4445,6 +4334,23 @@ int64_t bdrv_get_dirty_count(BlockDriverState *bs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get a reference to bs */
|
||||||
|
void bdrv_ref(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
bs->refcnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Release a previously grabbed reference to bs.
|
||||||
|
* If after releasing, reference count is zero, the BlockDriverState is
|
||||||
|
* deleted. */
|
||||||
|
void bdrv_unref(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
assert(bs->refcnt > 0);
|
||||||
|
if (--bs->refcnt == 0) {
|
||||||
|
bdrv_delete(bs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void bdrv_set_in_use(BlockDriverState *bs, int in_use)
|
void bdrv_set_in_use(BlockDriverState *bs, int in_use)
|
||||||
{
|
{
|
||||||
assert(bs->in_use != in_use);
|
assert(bs->in_use != in_use);
|
||||||
@ -4658,7 +4564,7 @@ out:
|
|||||||
free_option_parameters(param);
|
free_option_parameters(param);
|
||||||
|
|
||||||
if (bs) {
|
if (bs) {
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,14 +289,14 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
* backing file. */
|
* backing file. */
|
||||||
|
|
||||||
for (i = 0; i < BACKUP_SECTORS_PER_CLUSTER;) {
|
for (i = 0; i < BACKUP_SECTORS_PER_CLUSTER;) {
|
||||||
/* bdrv_co_is_allocated() only returns true/false based
|
/* bdrv_is_allocated() only returns true/false based
|
||||||
* on the first set of sectors it comes across that
|
* on the first set of sectors it comes across that
|
||||||
* are are all in the same state.
|
* are are all in the same state.
|
||||||
* For that reason we must verify each sector in the
|
* For that reason we must verify each sector in the
|
||||||
* backup cluster length. We end up copying more than
|
* backup cluster length. We end up copying more than
|
||||||
* needed but at some point that is always the case. */
|
* needed but at some point that is always the case. */
|
||||||
alloced =
|
alloced =
|
||||||
bdrv_co_is_allocated(bs,
|
bdrv_is_allocated(bs,
|
||||||
start * BACKUP_SECTORS_PER_CLUSTER + i,
|
start * BACKUP_SECTORS_PER_CLUSTER + i,
|
||||||
BACKUP_SECTORS_PER_CLUSTER - i, &n);
|
BACKUP_SECTORS_PER_CLUSTER - i, &n);
|
||||||
i += n;
|
i += n;
|
||||||
@ -338,7 +338,7 @@ static void coroutine_fn backup_run(void *opaque)
|
|||||||
hbitmap_free(job->bitmap);
|
hbitmap_free(job->bitmap);
|
||||||
|
|
||||||
bdrv_iostatus_disable(target);
|
bdrv_iostatus_disable(target);
|
||||||
bdrv_delete(target);
|
bdrv_unref(target);
|
||||||
|
|
||||||
block_job_completed(&job->common, ret);
|
block_job_completed(&job->common, ret);
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,7 @@ static int blkverify_open(BlockDriverState *bs, QDict *options, int flags)
|
|||||||
s->test_file = bdrv_new("");
|
s->test_file = bdrv_new("");
|
||||||
ret = bdrv_open(s->test_file, filename, NULL, flags, NULL);
|
ret = bdrv_open(s->test_file, filename, NULL, flags, NULL);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_delete(s->test_file);
|
bdrv_unref(s->test_file);
|
||||||
s->test_file = NULL;
|
s->test_file = NULL;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@ -169,7 +169,7 @@ static void blkverify_close(BlockDriverState *bs)
|
|||||||
{
|
{
|
||||||
BDRVBlkverifyState *s = bs->opaque;
|
BDRVBlkverifyState *s = bs->opaque;
|
||||||
|
|
||||||
bdrv_delete(s->test_file);
|
bdrv_unref(s->test_file);
|
||||||
s->test_file = NULL;
|
s->test_file = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,9 +108,9 @@ wait:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Copy if allocated above the base */
|
/* Copy if allocated above the base */
|
||||||
ret = bdrv_co_is_allocated_above(top, base, sector_num,
|
ret = bdrv_is_allocated_above(top, base, sector_num,
|
||||||
COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE,
|
COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE,
|
||||||
&n);
|
&n);
|
||||||
copy = (ret == 1);
|
copy = (ret == 1);
|
||||||
trace_commit_one_iteration(s, sector_num, n, ret);
|
trace_commit_one_iteration(s, sector_num, n, ret);
|
||||||
if (copy) {
|
if (copy) {
|
||||||
|
95
block/cow.c
95
block/cow.c
@ -106,7 +106,7 @@ static int cow_open(BlockDriverState *bs, QDict *options, int flags)
|
|||||||
* XXX(hch): right now these functions are extremely inefficient.
|
* XXX(hch): right now these functions are extremely inefficient.
|
||||||
* We should just read the whole bitmap we'll need in one go instead.
|
* We should just read the whole bitmap we'll need in one go instead.
|
||||||
*/
|
*/
|
||||||
static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum)
|
static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum, bool *first)
|
||||||
{
|
{
|
||||||
uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8;
|
uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8;
|
||||||
uint8_t bitmap;
|
uint8_t bitmap;
|
||||||
@ -117,27 +117,52 @@ static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (bitmap & (1 << (bitnum % 8))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*first) {
|
||||||
|
ret = bdrv_flush(bs->file);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
*first = false;
|
||||||
|
}
|
||||||
|
|
||||||
bitmap |= (1 << (bitnum % 8));
|
bitmap |= (1 << (bitnum % 8));
|
||||||
|
|
||||||
ret = bdrv_pwrite_sync(bs->file, offset, &bitmap, sizeof(bitmap));
|
ret = bdrv_pwrite(bs->file, offset, &bitmap, sizeof(bitmap));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum)
|
#define BITS_PER_BITMAP_SECTOR (512 * 8)
|
||||||
|
|
||||||
|
/* Cannot use bitmap.c on big-endian machines. */
|
||||||
|
static int cow_test_bit(int64_t bitnum, const uint8_t *bitmap)
|
||||||
{
|
{
|
||||||
uint64_t offset = sizeof(struct cow_header_v2) + bitnum / 8;
|
return (bitmap[bitnum / 8] & (1 << (bitnum & 7))) != 0;
|
||||||
uint8_t bitmap;
|
}
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
|
static int cow_find_streak(const uint8_t *bitmap, int value, int start, int nb_sectors)
|
||||||
if (ret < 0) {
|
{
|
||||||
return ret;
|
int streak_value = value ? 0xFF : 0;
|
||||||
|
int last = MIN(start + nb_sectors, BITS_PER_BITMAP_SECTOR);
|
||||||
|
int bitnum = start;
|
||||||
|
while (bitnum < last) {
|
||||||
|
if ((bitnum & 7) == 0 && bitmap[bitnum / 8] == streak_value) {
|
||||||
|
bitnum += 8;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (cow_test_bit(bitnum, bitmap) == value) {
|
||||||
|
bitnum++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
return MIN(bitnum, last) - start;
|
||||||
return !!(bitmap & (1 << (bitnum % 8)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return true if first block has been changed (ie. current version is
|
/* Return true if first block has been changed (ie. current version is
|
||||||
@ -146,34 +171,44 @@ static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum)
|
|||||||
static int coroutine_fn cow_co_is_allocated(BlockDriverState *bs,
|
static int coroutine_fn cow_co_is_allocated(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors, int *num_same)
|
int64_t sector_num, int nb_sectors, int *num_same)
|
||||||
{
|
{
|
||||||
|
int64_t bitnum = sector_num + sizeof(struct cow_header_v2) * 8;
|
||||||
|
uint64_t offset = (bitnum / 8) & -BDRV_SECTOR_SIZE;
|
||||||
|
uint8_t bitmap[BDRV_SECTOR_SIZE];
|
||||||
|
int ret;
|
||||||
int changed;
|
int changed;
|
||||||
|
|
||||||
if (nb_sectors == 0) {
|
ret = bdrv_pread(bs->file, offset, &bitmap, sizeof(bitmap));
|
||||||
*num_same = nb_sectors;
|
if (ret < 0) {
|
||||||
return 0;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
changed = is_bit_set(bs, sector_num);
|
|
||||||
if (changed < 0) {
|
|
||||||
return 0; /* XXX: how to return I/O errors? */
|
|
||||||
}
|
|
||||||
|
|
||||||
for (*num_same = 1; *num_same < nb_sectors; (*num_same)++) {
|
|
||||||
if (is_bit_set(bs, sector_num + *num_same) != changed)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bitnum &= BITS_PER_BITMAP_SECTOR - 1;
|
||||||
|
changed = cow_test_bit(bitnum, bitmap);
|
||||||
|
*num_same = cow_find_streak(bitmap, changed, bitnum, nb_sectors);
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int64_t coroutine_fn cow_co_get_block_status(BlockDriverState *bs,
|
||||||
|
int64_t sector_num, int nb_sectors, int *num_same)
|
||||||
|
{
|
||||||
|
BDRVCowState *s = bs->opaque;
|
||||||
|
int ret = cow_co_is_allocated(bs, sector_num, nb_sectors, num_same);
|
||||||
|
int64_t offset = s->cow_sectors_offset + (sector_num << BDRV_SECTOR_BITS);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return (ret ? BDRV_BLOCK_DATA : 0) | offset | BDRV_BLOCK_OFFSET_VALID;
|
||||||
|
}
|
||||||
|
|
||||||
static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
|
static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num,
|
||||||
int nb_sectors)
|
int nb_sectors)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
int i;
|
int i;
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
for (i = 0; i < nb_sectors; i++) {
|
for (i = 0; i < nb_sectors; i++) {
|
||||||
error = cow_set_bit(bs, sector_num + i);
|
error = cow_set_bit(bs, sector_num + i, &first);
|
||||||
if (error) {
|
if (error) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -189,7 +224,11 @@ static int coroutine_fn cow_read(BlockDriverState *bs, int64_t sector_num,
|
|||||||
int ret, n;
|
int ret, n;
|
||||||
|
|
||||||
while (nb_sectors > 0) {
|
while (nb_sectors > 0) {
|
||||||
if (bdrv_co_is_allocated(bs, sector_num, nb_sectors, &n)) {
|
ret = cow_co_is_allocated(bs, sector_num, nb_sectors, &n);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (ret) {
|
||||||
ret = bdrv_pread(bs->file,
|
ret = bdrv_pread(bs->file,
|
||||||
s->cow_sectors_offset + sector_num * 512,
|
s->cow_sectors_offset + sector_num * 512,
|
||||||
buf, n * 512);
|
buf, n * 512);
|
||||||
@ -314,7 +353,7 @@ static int cow_create(const char *filename, QEMUOptionParameter *options)
|
|||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
bdrv_delete(cow_bs);
|
bdrv_unref(cow_bs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,7 +383,7 @@ static BlockDriver bdrv_cow = {
|
|||||||
|
|
||||||
.bdrv_read = cow_co_read,
|
.bdrv_read = cow_co_read,
|
||||||
.bdrv_write = cow_co_write,
|
.bdrv_write = cow_co_write,
|
||||||
.bdrv_co_is_allocated = cow_co_is_allocated,
|
.bdrv_co_get_block_status = cow_co_get_block_status,
|
||||||
|
|
||||||
.create_options = cow_create_options,
|
.create_options = cow_create_options,
|
||||||
};
|
};
|
||||||
|
@ -1241,11 +1241,11 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options)
|
|||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int64_t total_size = 0;
|
int64_t total_size = 0;
|
||||||
BlockDriverState bs;
|
BlockDriverState *bs;
|
||||||
IscsiLun *iscsilun = NULL;
|
IscsiLun *iscsilun = NULL;
|
||||||
QDict *bs_options;
|
QDict *bs_options;
|
||||||
|
|
||||||
memset(&bs, 0, sizeof(BlockDriverState));
|
bs = bdrv_new("");
|
||||||
|
|
||||||
/* Read out options */
|
/* Read out options */
|
||||||
while (options && options->name) {
|
while (options && options->name) {
|
||||||
@ -1255,12 +1255,12 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options)
|
|||||||
options++;
|
options++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs.opaque = g_malloc0(sizeof(struct IscsiLun));
|
bs->opaque = g_malloc0(sizeof(struct IscsiLun));
|
||||||
iscsilun = bs.opaque;
|
iscsilun = bs->opaque;
|
||||||
|
|
||||||
bs_options = qdict_new();
|
bs_options = qdict_new();
|
||||||
qdict_put(bs_options, "filename", qstring_from_str(filename));
|
qdict_put(bs_options, "filename", qstring_from_str(filename));
|
||||||
ret = iscsi_open(&bs, bs_options, 0);
|
ret = iscsi_open(bs, bs_options, 0);
|
||||||
QDECREF(bs_options);
|
QDECREF(bs_options);
|
||||||
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
@ -1274,7 +1274,7 @@ static int iscsi_create(const char *filename, QEMUOptionParameter *options)
|
|||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (bs.total_sectors < total_size) {
|
if (bs->total_sectors < total_size) {
|
||||||
ret = -ENOSPC;
|
ret = -ENOSPC;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -1284,7 +1284,9 @@ out:
|
|||||||
if (iscsilun->iscsi != NULL) {
|
if (iscsilun->iscsi != NULL) {
|
||||||
iscsi_destroy_context(iscsilun->iscsi);
|
iscsi_destroy_context(iscsilun->iscsi);
|
||||||
}
|
}
|
||||||
g_free(bs.opaque);
|
g_free(bs->opaque);
|
||||||
|
bs->opaque = NULL;
|
||||||
|
bdrv_unref(bs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,8 +338,8 @@ static void coroutine_fn mirror_run(void *opaque)
|
|||||||
base = s->mode == MIRROR_SYNC_MODE_FULL ? NULL : bs->backing_hd;
|
base = s->mode == MIRROR_SYNC_MODE_FULL ? NULL : bs->backing_hd;
|
||||||
for (sector_num = 0; sector_num < end; ) {
|
for (sector_num = 0; sector_num < end; ) {
|
||||||
int64_t next = (sector_num | (sectors_per_chunk - 1)) + 1;
|
int64_t next = (sector_num | (sectors_per_chunk - 1)) + 1;
|
||||||
ret = bdrv_co_is_allocated_above(bs, base,
|
ret = bdrv_is_allocated_above(bs, base,
|
||||||
sector_num, next - sector_num, &n);
|
sector_num, next - sector_num, &n);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto immediate_exit;
|
goto immediate_exit;
|
||||||
@ -480,7 +480,7 @@ immediate_exit:
|
|||||||
bdrv_swap(s->target, s->common.bs);
|
bdrv_swap(s->target, s->common.bs);
|
||||||
}
|
}
|
||||||
bdrv_close(s->target);
|
bdrv_close(s->target);
|
||||||
bdrv_delete(s->target);
|
bdrv_unref(s->target);
|
||||||
block_job_completed(&s->common, ret);
|
block_job_completed(&s->common, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
50
block/qapi.c
50
block/qapi.c
@ -223,18 +223,44 @@ void bdrv_query_info(BlockDriverState *bs,
|
|||||||
info->inserted->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
info->inserted->backing_file_depth = bdrv_get_backing_file_depth(bs);
|
||||||
|
|
||||||
if (bs->io_limits_enabled) {
|
if (bs->io_limits_enabled) {
|
||||||
info->inserted->bps =
|
ThrottleConfig cfg;
|
||||||
bs->io_limits.bps[BLOCK_IO_LIMIT_TOTAL];
|
throttle_get_config(&bs->throttle_state, &cfg);
|
||||||
info->inserted->bps_rd =
|
info->inserted->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg;
|
||||||
bs->io_limits.bps[BLOCK_IO_LIMIT_READ];
|
info->inserted->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg;
|
||||||
info->inserted->bps_wr =
|
info->inserted->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg;
|
||||||
bs->io_limits.bps[BLOCK_IO_LIMIT_WRITE];
|
|
||||||
info->inserted->iops =
|
info->inserted->iops = cfg.buckets[THROTTLE_OPS_TOTAL].avg;
|
||||||
bs->io_limits.iops[BLOCK_IO_LIMIT_TOTAL];
|
info->inserted->iops_rd = cfg.buckets[THROTTLE_OPS_READ].avg;
|
||||||
info->inserted->iops_rd =
|
info->inserted->iops_wr = cfg.buckets[THROTTLE_OPS_WRITE].avg;
|
||||||
bs->io_limits.iops[BLOCK_IO_LIMIT_READ];
|
|
||||||
info->inserted->iops_wr =
|
info->inserted->has_bps_max =
|
||||||
bs->io_limits.iops[BLOCK_IO_LIMIT_WRITE];
|
cfg.buckets[THROTTLE_BPS_TOTAL].max;
|
||||||
|
info->inserted->bps_max =
|
||||||
|
cfg.buckets[THROTTLE_BPS_TOTAL].max;
|
||||||
|
info->inserted->has_bps_rd_max =
|
||||||
|
cfg.buckets[THROTTLE_BPS_READ].max;
|
||||||
|
info->inserted->bps_rd_max =
|
||||||
|
cfg.buckets[THROTTLE_BPS_READ].max;
|
||||||
|
info->inserted->has_bps_wr_max =
|
||||||
|
cfg.buckets[THROTTLE_BPS_WRITE].max;
|
||||||
|
info->inserted->bps_wr_max =
|
||||||
|
cfg.buckets[THROTTLE_BPS_WRITE].max;
|
||||||
|
|
||||||
|
info->inserted->has_iops_max =
|
||||||
|
cfg.buckets[THROTTLE_OPS_TOTAL].max;
|
||||||
|
info->inserted->iops_max =
|
||||||
|
cfg.buckets[THROTTLE_OPS_TOTAL].max;
|
||||||
|
info->inserted->has_iops_rd_max =
|
||||||
|
cfg.buckets[THROTTLE_OPS_READ].max;
|
||||||
|
info->inserted->iops_rd_max =
|
||||||
|
cfg.buckets[THROTTLE_OPS_READ].max;
|
||||||
|
info->inserted->has_iops_wr_max =
|
||||||
|
cfg.buckets[THROTTLE_OPS_WRITE].max;
|
||||||
|
info->inserted->iops_wr_max =
|
||||||
|
cfg.buckets[THROTTLE_OPS_WRITE].max;
|
||||||
|
|
||||||
|
info->inserted->has_iops_size = cfg.op_size;
|
||||||
|
info->inserted->iops_size = cfg.op_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs0 = bs;
|
bs0 = bs;
|
||||||
|
15
block/qcow.c
15
block/qcow.c
@ -395,7 +395,7 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
|
|||||||
return cluster_offset;
|
return cluster_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn qcow_co_is_allocated(BlockDriverState *bs,
|
static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors, int *pnum)
|
int64_t sector_num, int nb_sectors, int *pnum)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
@ -410,7 +410,14 @@ static int coroutine_fn qcow_co_is_allocated(BlockDriverState *bs,
|
|||||||
if (n > nb_sectors)
|
if (n > nb_sectors)
|
||||||
n = nb_sectors;
|
n = nb_sectors;
|
||||||
*pnum = n;
|
*pnum = n;
|
||||||
return (cluster_offset != 0);
|
if (!cluster_offset) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if ((cluster_offset & QCOW_OFLAG_COMPRESSED) || s->crypt_method) {
|
||||||
|
return BDRV_BLOCK_DATA;
|
||||||
|
}
|
||||||
|
cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
|
||||||
|
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | cluster_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
|
static int decompress_buffer(uint8_t *out_buf, int out_buf_size,
|
||||||
@ -751,7 +758,7 @@ static int qcow_create(const char *filename, QEMUOptionParameter *options)
|
|||||||
g_free(tmp);
|
g_free(tmp);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
exit:
|
exit:
|
||||||
bdrv_delete(qcow_bs);
|
bdrv_unref(qcow_bs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -896,7 +903,7 @@ static BlockDriver bdrv_qcow = {
|
|||||||
|
|
||||||
.bdrv_co_readv = qcow_co_readv,
|
.bdrv_co_readv = qcow_co_readv,
|
||||||
.bdrv_co_writev = qcow_co_writev,
|
.bdrv_co_writev = qcow_co_writev,
|
||||||
.bdrv_co_is_allocated = qcow_co_is_allocated,
|
.bdrv_co_get_block_status = qcow_co_get_block_status,
|
||||||
|
|
||||||
.bdrv_set_key = qcow_set_key,
|
.bdrv_set_key = qcow_set_key,
|
||||||
.bdrv_make_empty = qcow_make_empty,
|
.bdrv_make_empty = qcow_make_empty,
|
||||||
|
@ -688,24 +688,34 @@ static int qcow2_reopen_prepare(BDRVReopenState *state,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn qcow2_co_is_allocated(BlockDriverState *bs,
|
static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors, int *pnum)
|
int64_t sector_num, int nb_sectors, int *pnum)
|
||||||
{
|
{
|
||||||
BDRVQcowState *s = bs->opaque;
|
BDRVQcowState *s = bs->opaque;
|
||||||
uint64_t cluster_offset;
|
uint64_t cluster_offset;
|
||||||
int ret;
|
int index_in_cluster, ret;
|
||||||
|
int64_t status = 0;
|
||||||
|
|
||||||
*pnum = nb_sectors;
|
*pnum = nb_sectors;
|
||||||
/* FIXME We can get errors here, but the bdrv_co_is_allocated interface
|
|
||||||
* can't pass them on today */
|
|
||||||
qemu_co_mutex_lock(&s->lock);
|
qemu_co_mutex_lock(&s->lock);
|
||||||
ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset);
|
ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset);
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
*pnum = 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (cluster_offset != 0) || (ret == QCOW2_CLUSTER_ZERO);
|
if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
|
||||||
|
!s->crypt_method) {
|
||||||
|
index_in_cluster = sector_num & (s->cluster_sectors - 1);
|
||||||
|
cluster_offset |= (index_in_cluster << BDRV_SECTOR_BITS);
|
||||||
|
status |= BDRV_BLOCK_OFFSET_VALID | cluster_offset;
|
||||||
|
}
|
||||||
|
if (ret == QCOW2_CLUSTER_ZERO) {
|
||||||
|
status |= BDRV_BLOCK_ZERO;
|
||||||
|
} else if (ret != QCOW2_CLUSTER_UNALLOCATED) {
|
||||||
|
status |= BDRV_BLOCK_DATA;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* handle reading after the end of the backing file */
|
/* handle reading after the end of the backing file */
|
||||||
@ -1452,7 +1462,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
|||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1868,7 +1878,7 @@ static BlockDriver bdrv_qcow2 = {
|
|||||||
.bdrv_reopen_prepare = qcow2_reopen_prepare,
|
.bdrv_reopen_prepare = qcow2_reopen_prepare,
|
||||||
.bdrv_create = qcow2_create,
|
.bdrv_create = qcow2_create,
|
||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_co_is_allocated = qcow2_co_is_allocated,
|
.bdrv_co_get_block_status = qcow2_co_get_block_status,
|
||||||
.bdrv_set_key = qcow2_set_key,
|
.bdrv_set_key = qcow2_set_key,
|
||||||
.bdrv_make_empty = qcow2_make_empty,
|
.bdrv_make_empty = qcow2_make_empty,
|
||||||
|
|
||||||
|
41
block/qed.c
41
block/qed.c
@ -599,7 +599,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
|
|||||||
ret = 0; /* success */
|
ret = 0; /* success */
|
||||||
out:
|
out:
|
||||||
g_free(l1_table);
|
g_free(l1_table);
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,45 +652,66 @@ static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options)
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
BlockDriverState *bs;
|
||||||
Coroutine *co;
|
Coroutine *co;
|
||||||
int is_allocated;
|
uint64_t pos;
|
||||||
|
int64_t status;
|
||||||
int *pnum;
|
int *pnum;
|
||||||
} QEDIsAllocatedCB;
|
} QEDIsAllocatedCB;
|
||||||
|
|
||||||
static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len)
|
static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t len)
|
||||||
{
|
{
|
||||||
QEDIsAllocatedCB *cb = opaque;
|
QEDIsAllocatedCB *cb = opaque;
|
||||||
|
BDRVQEDState *s = cb->bs->opaque;
|
||||||
*cb->pnum = len / BDRV_SECTOR_SIZE;
|
*cb->pnum = len / BDRV_SECTOR_SIZE;
|
||||||
cb->is_allocated = (ret == QED_CLUSTER_FOUND || ret == QED_CLUSTER_ZERO);
|
switch (ret) {
|
||||||
|
case QED_CLUSTER_FOUND:
|
||||||
|
offset |= qed_offset_into_cluster(s, cb->pos);
|
||||||
|
cb->status = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
|
||||||
|
break;
|
||||||
|
case QED_CLUSTER_ZERO:
|
||||||
|
cb->status = BDRV_BLOCK_ZERO;
|
||||||
|
break;
|
||||||
|
case QED_CLUSTER_L2:
|
||||||
|
case QED_CLUSTER_L1:
|
||||||
|
cb->status = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(ret < 0);
|
||||||
|
cb->status = ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (cb->co) {
|
if (cb->co) {
|
||||||
qemu_coroutine_enter(cb->co, NULL);
|
qemu_coroutine_enter(cb->co, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn bdrv_qed_co_is_allocated(BlockDriverState *bs,
|
static int64_t coroutine_fn bdrv_qed_co_get_block_status(BlockDriverState *bs,
|
||||||
int64_t sector_num,
|
int64_t sector_num,
|
||||||
int nb_sectors, int *pnum)
|
int nb_sectors, int *pnum)
|
||||||
{
|
{
|
||||||
BDRVQEDState *s = bs->opaque;
|
BDRVQEDState *s = bs->opaque;
|
||||||
uint64_t pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE;
|
|
||||||
size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE;
|
size_t len = (size_t)nb_sectors * BDRV_SECTOR_SIZE;
|
||||||
QEDIsAllocatedCB cb = {
|
QEDIsAllocatedCB cb = {
|
||||||
.is_allocated = -1,
|
.bs = bs,
|
||||||
|
.pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE,
|
||||||
|
.status = BDRV_BLOCK_OFFSET_MASK,
|
||||||
.pnum = pnum,
|
.pnum = pnum,
|
||||||
};
|
};
|
||||||
QEDRequest request = { .l2_table = NULL };
|
QEDRequest request = { .l2_table = NULL };
|
||||||
|
|
||||||
qed_find_cluster(s, &request, pos, len, qed_is_allocated_cb, &cb);
|
qed_find_cluster(s, &request, cb.pos, len, qed_is_allocated_cb, &cb);
|
||||||
|
|
||||||
/* Now sleep if the callback wasn't invoked immediately */
|
/* Now sleep if the callback wasn't invoked immediately */
|
||||||
while (cb.is_allocated == -1) {
|
while (cb.status == BDRV_BLOCK_OFFSET_MASK) {
|
||||||
cb.co = qemu_coroutine_self();
|
cb.co = qemu_coroutine_self();
|
||||||
qemu_coroutine_yield();
|
qemu_coroutine_yield();
|
||||||
}
|
}
|
||||||
|
|
||||||
qed_unref_l2_cache_entry(request.l2_table);
|
qed_unref_l2_cache_entry(request.l2_table);
|
||||||
|
|
||||||
return cb.is_allocated;
|
return cb.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bdrv_qed_make_empty(BlockDriverState *bs)
|
static int bdrv_qed_make_empty(BlockDriverState *bs)
|
||||||
@ -1575,7 +1596,7 @@ static BlockDriver bdrv_qed = {
|
|||||||
.bdrv_reopen_prepare = bdrv_qed_reopen_prepare,
|
.bdrv_reopen_prepare = bdrv_qed_reopen_prepare,
|
||||||
.bdrv_create = bdrv_qed_create,
|
.bdrv_create = bdrv_qed_create,
|
||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_co_is_allocated = bdrv_qed_co_is_allocated,
|
.bdrv_co_get_block_status = bdrv_qed_co_get_block_status,
|
||||||
.bdrv_make_empty = bdrv_qed_make_empty,
|
.bdrv_make_empty = bdrv_qed_make_empty,
|
||||||
.bdrv_aio_readv = bdrv_qed_aio_readv,
|
.bdrv_aio_readv = bdrv_qed_aio_readv,
|
||||||
.bdrv_aio_writev = bdrv_qed_aio_writev,
|
.bdrv_aio_writev = bdrv_qed_aio_writev,
|
||||||
|
@ -1084,12 +1084,12 @@ static int raw_create(const char *filename, QEMUOptionParameter *options)
|
|||||||
* 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes
|
* 'nb_sectors' is the max value 'pnum' should be set to. If nb_sectors goes
|
||||||
* beyond the end of the disk image it will be clamped.
|
* beyond the end of the disk image it will be clamped.
|
||||||
*/
|
*/
|
||||||
static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
|
static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
|
||||||
int64_t sector_num,
|
int64_t sector_num,
|
||||||
int nb_sectors, int *pnum)
|
int nb_sectors, int *pnum)
|
||||||
{
|
{
|
||||||
off_t start, data, hole;
|
off_t start, data, hole;
|
||||||
int ret;
|
int64_t ret;
|
||||||
|
|
||||||
ret = fd_open(bs);
|
ret = fd_open(bs);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -1097,6 +1097,7 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
|
|
||||||
start = sector_num * BDRV_SECTOR_SIZE;
|
start = sector_num * BDRV_SECTOR_SIZE;
|
||||||
|
ret = BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | start;
|
||||||
|
|
||||||
#ifdef CONFIG_FIEMAP
|
#ifdef CONFIG_FIEMAP
|
||||||
|
|
||||||
@ -1114,7 +1115,7 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
|
|||||||
if (ioctl(s->fd, FS_IOC_FIEMAP, &f) == -1) {
|
if (ioctl(s->fd, FS_IOC_FIEMAP, &f) == -1) {
|
||||||
/* Assume everything is allocated. */
|
/* Assume everything is allocated. */
|
||||||
*pnum = nb_sectors;
|
*pnum = nb_sectors;
|
||||||
return 1;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f.fm.fm_mapped_extents == 0) {
|
if (f.fm.fm_mapped_extents == 0) {
|
||||||
@ -1127,6 +1128,9 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
|
|||||||
} else {
|
} else {
|
||||||
data = f.fe.fe_logical;
|
data = f.fe.fe_logical;
|
||||||
hole = f.fe.fe_logical + f.fe.fe_length;
|
hole = f.fe.fe_logical + f.fe.fe_length;
|
||||||
|
if (f.fe.fe_flags & FIEMAP_EXTENT_UNWRITTEN) {
|
||||||
|
ret |= BDRV_BLOCK_ZERO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif defined SEEK_HOLE && defined SEEK_DATA
|
#elif defined SEEK_HOLE && defined SEEK_DATA
|
||||||
@ -1141,7 +1145,7 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
|
|||||||
|
|
||||||
/* Most likely EINVAL. Assume everything is allocated. */
|
/* Most likely EINVAL. Assume everything is allocated. */
|
||||||
*pnum = nb_sectors;
|
*pnum = nb_sectors;
|
||||||
return 1;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hole > start) {
|
if (hole > start) {
|
||||||
@ -1154,19 +1158,21 @@ static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
*pnum = nb_sectors;
|
data = 0;
|
||||||
return 1;
|
hole = start + nb_sectors * BDRV_SECTOR_SIZE;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (data <= start) {
|
if (data <= start) {
|
||||||
/* On a data extent, compute sectors to the end of the extent. */
|
/* On a data extent, compute sectors to the end of the extent. */
|
||||||
*pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE);
|
*pnum = MIN(nb_sectors, (hole - start) / BDRV_SECTOR_SIZE);
|
||||||
return 1;
|
|
||||||
} else {
|
} else {
|
||||||
/* On a hole, compute sectors to the beginning of the next extent. */
|
/* On a hole, compute sectors to the beginning of the next extent. */
|
||||||
*pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE);
|
*pnum = MIN(nb_sectors, (data - start) / BDRV_SECTOR_SIZE);
|
||||||
return 0;
|
ret &= ~BDRV_BLOCK_DATA;
|
||||||
|
ret |= BDRV_BLOCK_ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static coroutine_fn BlockDriverAIOCB *raw_aio_discard(BlockDriverState *bs,
|
static coroutine_fn BlockDriverAIOCB *raw_aio_discard(BlockDriverState *bs,
|
||||||
@ -1200,7 +1206,7 @@ static BlockDriver bdrv_file = {
|
|||||||
.bdrv_close = raw_close,
|
.bdrv_close = raw_close,
|
||||||
.bdrv_create = raw_create,
|
.bdrv_create = raw_create,
|
||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_co_is_allocated = raw_co_is_allocated,
|
.bdrv_co_get_block_status = raw_co_get_block_status,
|
||||||
|
|
||||||
.bdrv_aio_readv = raw_aio_readv,
|
.bdrv_aio_readv = raw_aio_readv,
|
||||||
.bdrv_aio_writev = raw_aio_writev,
|
.bdrv_aio_writev = raw_aio_writev,
|
||||||
|
@ -535,13 +535,29 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags)
|
|||||||
{
|
{
|
||||||
BDRVRawState *s = bs->opaque;
|
BDRVRawState *s = bs->opaque;
|
||||||
int access_flags, create_flags;
|
int access_flags, create_flags;
|
||||||
|
int ret = 0;
|
||||||
DWORD overlapped;
|
DWORD overlapped;
|
||||||
char device_name[64];
|
char device_name[64];
|
||||||
const char *filename = qdict_get_str(options, "filename");
|
|
||||||
|
Error *local_err = NULL;
|
||||||
|
const char *filename;
|
||||||
|
|
||||||
|
QemuOpts *opts = qemu_opts_create_nofail(&raw_runtime_opts);
|
||||||
|
qemu_opts_absorb_qdict(opts, options, &local_err);
|
||||||
|
if (error_is_set(&local_err)) {
|
||||||
|
qerror_report_err(local_err);
|
||||||
|
error_free(local_err);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = qemu_opt_get(opts, "filename");
|
||||||
|
|
||||||
if (strstart(filename, "/dev/cdrom", NULL)) {
|
if (strstart(filename, "/dev/cdrom", NULL)) {
|
||||||
if (find_cdrom(device_name, sizeof(device_name)) < 0)
|
if (find_cdrom(device_name, sizeof(device_name)) < 0) {
|
||||||
return -ENOENT;
|
ret = -ENOENT;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
filename = device_name;
|
filename = device_name;
|
||||||
} else {
|
} else {
|
||||||
/* transform drive letters into device name */
|
/* transform drive letters into device name */
|
||||||
@ -564,11 +580,17 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags)
|
|||||||
if (s->hfile == INVALID_HANDLE_VALUE) {
|
if (s->hfile == INVALID_HANDLE_VALUE) {
|
||||||
int err = GetLastError();
|
int err = GetLastError();
|
||||||
|
|
||||||
if (err == ERROR_ACCESS_DENIED)
|
if (err == ERROR_ACCESS_DENIED) {
|
||||||
return -EACCES;
|
ret = -EACCES;
|
||||||
return -1;
|
} else {
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
done:
|
||||||
|
qemu_opts_del(opts);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockDriver bdrv_host_device = {
|
static BlockDriver bdrv_host_device = {
|
||||||
|
@ -58,11 +58,11 @@ static int coroutine_fn raw_co_writev(BlockDriverState *bs, int64_t sector_num,
|
|||||||
return bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov);
|
return bdrv_co_writev(bs->file, sector_num, nb_sectors, qiov);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn raw_co_is_allocated(BlockDriverState *bs,
|
static int64_t coroutine_fn raw_co_get_block_status(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors,
|
int64_t sector_num,
|
||||||
int *pnum)
|
int nb_sectors, int *pnum)
|
||||||
{
|
{
|
||||||
return bdrv_co_is_allocated(bs->file, sector_num, nb_sectors, pnum);
|
return bdrv_get_block_status(bs->file, sector_num, nb_sectors, pnum);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn raw_co_write_zeroes(BlockDriverState *bs,
|
static int coroutine_fn raw_co_write_zeroes(BlockDriverState *bs,
|
||||||
@ -164,7 +164,7 @@ static BlockDriver bdrv_raw = {
|
|||||||
.bdrv_co_writev = &raw_co_writev,
|
.bdrv_co_writev = &raw_co_writev,
|
||||||
.bdrv_co_write_zeroes = &raw_co_write_zeroes,
|
.bdrv_co_write_zeroes = &raw_co_write_zeroes,
|
||||||
.bdrv_co_discard = &raw_co_discard,
|
.bdrv_co_discard = &raw_co_discard,
|
||||||
.bdrv_co_is_allocated = &raw_co_is_allocated,
|
.bdrv_co_get_block_status = &raw_co_get_block_status,
|
||||||
.bdrv_truncate = &raw_truncate,
|
.bdrv_truncate = &raw_truncate,
|
||||||
.bdrv_getlength = &raw_getlength,
|
.bdrv_getlength = &raw_getlength,
|
||||||
.bdrv_get_info = &raw_get_info,
|
.bdrv_get_info = &raw_get_info,
|
||||||
|
@ -1430,7 +1430,7 @@ static int sd_prealloc(const char *filename)
|
|||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
if (bs) {
|
if (bs) {
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
}
|
}
|
||||||
g_free(buf);
|
g_free(buf);
|
||||||
|
|
||||||
@ -1509,13 +1509,13 @@ static int sd_create(const char *filename, QEMUOptionParameter *options)
|
|||||||
|
|
||||||
if (!is_snapshot(&s->inode)) {
|
if (!is_snapshot(&s->inode)) {
|
||||||
error_report("cannot clone from a non snapshot vdi");
|
error_report("cannot clone from a non snapshot vdi");
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
base_vid = s->inode.vdi_id;
|
base_vid = s->inode.vdi_id;
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = do_sd_create(s, vdi, vdi_size, base_vid, &vid, 0);
|
ret = do_sd_create(s, vdi, vdi_size, base_vid, &vid, 0);
|
||||||
@ -2270,9 +2270,9 @@ static coroutine_fn int sd_co_discard(BlockDriverState *bs, int64_t sector_num,
|
|||||||
return acb->ret;
|
return acb->ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static coroutine_fn int
|
static coroutine_fn int64_t
|
||||||
sd_co_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
sd_co_get_block_status(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||||
int *pnum)
|
int *pnum)
|
||||||
{
|
{
|
||||||
BDRVSheepdogState *s = bs->opaque;
|
BDRVSheepdogState *s = bs->opaque;
|
||||||
SheepdogInode *inode = &s->inode;
|
SheepdogInode *inode = &s->inode;
|
||||||
@ -2280,7 +2280,7 @@ sd_co_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
|||||||
end = DIV_ROUND_UP((sector_num + nb_sectors) *
|
end = DIV_ROUND_UP((sector_num + nb_sectors) *
|
||||||
BDRV_SECTOR_SIZE, SD_DATA_OBJ_SIZE);
|
BDRV_SECTOR_SIZE, SD_DATA_OBJ_SIZE);
|
||||||
unsigned long idx;
|
unsigned long idx;
|
||||||
int ret = 1;
|
int64_t ret = BDRV_BLOCK_DATA;
|
||||||
|
|
||||||
for (idx = start; idx < end; idx++) {
|
for (idx = start; idx < end; idx++) {
|
||||||
if (inode->data_vdi_id[idx] == 0) {
|
if (inode->data_vdi_id[idx] == 0) {
|
||||||
@ -2338,7 +2338,7 @@ static BlockDriver bdrv_sheepdog = {
|
|||||||
.bdrv_co_writev = sd_co_writev,
|
.bdrv_co_writev = sd_co_writev,
|
||||||
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
||||||
.bdrv_co_discard = sd_co_discard,
|
.bdrv_co_discard = sd_co_discard,
|
||||||
.bdrv_co_is_allocated = sd_co_is_allocated,
|
.bdrv_co_get_block_status = sd_co_get_block_status,
|
||||||
|
|
||||||
.bdrv_snapshot_create = sd_snapshot_create,
|
.bdrv_snapshot_create = sd_snapshot_create,
|
||||||
.bdrv_snapshot_goto = sd_snapshot_goto,
|
.bdrv_snapshot_goto = sd_snapshot_goto,
|
||||||
@ -2366,7 +2366,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
|
|||||||
.bdrv_co_writev = sd_co_writev,
|
.bdrv_co_writev = sd_co_writev,
|
||||||
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
||||||
.bdrv_co_discard = sd_co_discard,
|
.bdrv_co_discard = sd_co_discard,
|
||||||
.bdrv_co_is_allocated = sd_co_is_allocated,
|
.bdrv_co_get_block_status = sd_co_get_block_status,
|
||||||
|
|
||||||
.bdrv_snapshot_create = sd_snapshot_create,
|
.bdrv_snapshot_create = sd_snapshot_create,
|
||||||
.bdrv_snapshot_goto = sd_snapshot_goto,
|
.bdrv_snapshot_goto = sd_snapshot_goto,
|
||||||
@ -2394,7 +2394,7 @@ static BlockDriver bdrv_sheepdog_unix = {
|
|||||||
.bdrv_co_writev = sd_co_writev,
|
.bdrv_co_writev = sd_co_writev,
|
||||||
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
.bdrv_co_flush_to_disk = sd_co_flush_to_disk,
|
||||||
.bdrv_co_discard = sd_co_discard,
|
.bdrv_co_discard = sd_co_discard,
|
||||||
.bdrv_co_is_allocated = sd_co_is_allocated,
|
.bdrv_co_get_block_status = sd_co_get_block_status,
|
||||||
|
|
||||||
.bdrv_snapshot_create = sd_snapshot_create,
|
.bdrv_snapshot_create = sd_snapshot_create,
|
||||||
.bdrv_snapshot_goto = sd_snapshot_goto,
|
.bdrv_snapshot_goto = sd_snapshot_goto,
|
||||||
|
@ -99,7 +99,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
|
|||||||
ret = bdrv_snapshot_goto(bs->file, snapshot_id);
|
ret = bdrv_snapshot_goto(bs->file, snapshot_id);
|
||||||
open_ret = drv->bdrv_open(bs, NULL, bs->open_flags);
|
open_ret = drv->bdrv_open(bs, NULL, bs->open_flags);
|
||||||
if (open_ret < 0) {
|
if (open_ret < 0) {
|
||||||
bdrv_delete(bs->file);
|
bdrv_unref(bs->file);
|
||||||
bs->drv = NULL;
|
bs->drv = NULL;
|
||||||
return open_ret;
|
return open_ret;
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ static void close_unused_images(BlockDriverState *top, BlockDriverState *base,
|
|||||||
unused = intermediate;
|
unused = intermediate;
|
||||||
intermediate = intermediate->backing_hd;
|
intermediate = intermediate->backing_hd;
|
||||||
unused->backing_hd = NULL;
|
unused->backing_hd = NULL;
|
||||||
bdrv_delete(unused);
|
bdrv_unref(unused);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,16 +119,16 @@ wait:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = bdrv_co_is_allocated(bs, sector_num,
|
ret = bdrv_is_allocated(bs, sector_num,
|
||||||
STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n);
|
STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n);
|
||||||
if (ret == 1) {
|
if (ret == 1) {
|
||||||
/* Allocated in the top, no need to copy. */
|
/* Allocated in the top, no need to copy. */
|
||||||
copy = false;
|
copy = false;
|
||||||
} else {
|
} else if (ret >= 0) {
|
||||||
/* Copy if allocated in the intermediate images. Limit to the
|
/* Copy if allocated in the intermediate images. Limit to the
|
||||||
* known-unallocated area [sector_num, sector_num+n). */
|
* known-unallocated area [sector_num, sector_num+n). */
|
||||||
ret = bdrv_co_is_allocated_above(bs->backing_hd, base,
|
ret = bdrv_is_allocated_above(bs->backing_hd, base,
|
||||||
sector_num, n, &n);
|
sector_num, n, &n);
|
||||||
|
|
||||||
/* Finish early if end of backing file has been reached */
|
/* Finish early if end of backing file has been reached */
|
||||||
if (ret == 0 && n == 0) {
|
if (ret == 0 && n == 0) {
|
||||||
|
17
block/vdi.c
17
block/vdi.c
@ -470,7 +470,7 @@ static int vdi_reopen_prepare(BDRVReopenState *state,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn vdi_co_is_allocated(BlockDriverState *bs,
|
static int64_t coroutine_fn vdi_co_get_block_status(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors, int *pnum)
|
int64_t sector_num, int nb_sectors, int *pnum)
|
||||||
{
|
{
|
||||||
/* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */
|
/* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */
|
||||||
@ -479,12 +479,23 @@ static int coroutine_fn vdi_co_is_allocated(BlockDriverState *bs,
|
|||||||
size_t sector_in_block = sector_num % s->block_sectors;
|
size_t sector_in_block = sector_num % s->block_sectors;
|
||||||
int n_sectors = s->block_sectors - sector_in_block;
|
int n_sectors = s->block_sectors - sector_in_block;
|
||||||
uint32_t bmap_entry = le32_to_cpu(s->bmap[bmap_index]);
|
uint32_t bmap_entry = le32_to_cpu(s->bmap[bmap_index]);
|
||||||
|
uint64_t offset;
|
||||||
|
int result;
|
||||||
|
|
||||||
logout("%p, %" PRId64 ", %d, %p\n", bs, sector_num, nb_sectors, pnum);
|
logout("%p, %" PRId64 ", %d, %p\n", bs, sector_num, nb_sectors, pnum);
|
||||||
if (n_sectors > nb_sectors) {
|
if (n_sectors > nb_sectors) {
|
||||||
n_sectors = nb_sectors;
|
n_sectors = nb_sectors;
|
||||||
}
|
}
|
||||||
*pnum = n_sectors;
|
*pnum = n_sectors;
|
||||||
return VDI_IS_ALLOCATED(bmap_entry);
|
result = VDI_IS_ALLOCATED(bmap_entry);
|
||||||
|
if (!result) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset = s->header.offset_data +
|
||||||
|
(uint64_t)bmap_entry * s->block_size +
|
||||||
|
sector_in_block * SECTOR_SIZE;
|
||||||
|
return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID | offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vdi_co_read(BlockDriverState *bs,
|
static int vdi_co_read(BlockDriverState *bs,
|
||||||
@ -780,7 +791,7 @@ static BlockDriver bdrv_vdi = {
|
|||||||
.bdrv_reopen_prepare = vdi_reopen_prepare,
|
.bdrv_reopen_prepare = vdi_reopen_prepare,
|
||||||
.bdrv_create = vdi_create,
|
.bdrv_create = vdi_create,
|
||||||
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
.bdrv_has_zero_init = bdrv_has_zero_init_1,
|
||||||
.bdrv_co_is_allocated = vdi_co_is_allocated,
|
.bdrv_co_get_block_status = vdi_co_get_block_status,
|
||||||
.bdrv_make_empty = vdi_make_empty,
|
.bdrv_make_empty = vdi_make_empty,
|
||||||
|
|
||||||
.bdrv_read = vdi_co_read,
|
.bdrv_read = vdi_co_read,
|
||||||
|
33
block/vmdk.c
33
block/vmdk.c
@ -216,7 +216,7 @@ static void vmdk_free_extents(BlockDriverState *bs)
|
|||||||
g_free(e->l2_cache);
|
g_free(e->l2_cache);
|
||||||
g_free(e->l1_backup_table);
|
g_free(e->l1_backup_table);
|
||||||
if (e->file != bs->file) {
|
if (e->file != bs->file) {
|
||||||
bdrv_delete(e->file);
|
bdrv_unref(e->file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_free(s->extents);
|
g_free(s->extents);
|
||||||
@ -746,7 +746,7 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs,
|
|||||||
/* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/
|
/* SPARSE extent and VMFSSPARSE extent are both "COWD" sparse file*/
|
||||||
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags);
|
ret = vmdk_open_sparse(bs, extent_file, bs->open_flags);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
bdrv_delete(extent_file);
|
bdrv_unref(extent_file);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1042,7 +1042,7 @@ static VmdkExtent *find_extent(BDRVVmdkState *s,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn vmdk_co_is_allocated(BlockDriverState *bs,
|
static int64_t coroutine_fn vmdk_co_get_block_status(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors, int *pnum)
|
int64_t sector_num, int nb_sectors, int *pnum)
|
||||||
{
|
{
|
||||||
BDRVVmdkState *s = bs->opaque;
|
BDRVVmdkState *s = bs->opaque;
|
||||||
@ -1059,7 +1059,24 @@ static int coroutine_fn vmdk_co_is_allocated(BlockDriverState *bs,
|
|||||||
sector_num * 512, 0, &offset);
|
sector_num * 512, 0, &offset);
|
||||||
qemu_co_mutex_unlock(&s->lock);
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
|
|
||||||
ret = (ret == VMDK_OK || ret == VMDK_ZEROED);
|
switch (ret) {
|
||||||
|
case VMDK_ERROR:
|
||||||
|
ret = -EIO;
|
||||||
|
break;
|
||||||
|
case VMDK_UNALLOC:
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
case VMDK_ZEROED:
|
||||||
|
ret = BDRV_BLOCK_ZERO;
|
||||||
|
break;
|
||||||
|
case VMDK_OK:
|
||||||
|
ret = BDRV_BLOCK_DATA;
|
||||||
|
if (extent->file == bs->file) {
|
||||||
|
ret |= BDRV_BLOCK_OFFSET_VALID | offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
index_in_cluster = sector_num % extent->cluster_sectors;
|
index_in_cluster = sector_num % extent->cluster_sectors;
|
||||||
n = extent->cluster_sectors - index_in_cluster;
|
n = extent->cluster_sectors - index_in_cluster;
|
||||||
@ -1636,15 +1653,15 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options)
|
|||||||
BlockDriverState *bs = bdrv_new("");
|
BlockDriverState *bs = bdrv_new("");
|
||||||
ret = bdrv_open(bs, backing_file, NULL, 0, NULL);
|
ret = bdrv_open(bs, backing_file, NULL, 0, NULL);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (strcmp(bs->drv->format_name, "vmdk")) {
|
if (strcmp(bs->drv->format_name, "vmdk")) {
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
parent_cid = vmdk_read_cid(bs, 0);
|
parent_cid = vmdk_read_cid(bs, 0);
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
snprintf(parent_desc_line, sizeof(parent_desc_line),
|
snprintf(parent_desc_line, sizeof(parent_desc_line),
|
||||||
"parentFileNameHint=\"%s\"", backing_file);
|
"parentFileNameHint=\"%s\"", backing_file);
|
||||||
}
|
}
|
||||||
@ -1837,7 +1854,7 @@ static BlockDriver bdrv_vmdk = {
|
|||||||
.bdrv_close = vmdk_close,
|
.bdrv_close = vmdk_close,
|
||||||
.bdrv_create = vmdk_create,
|
.bdrv_create = vmdk_create,
|
||||||
.bdrv_co_flush_to_disk = vmdk_co_flush,
|
.bdrv_co_flush_to_disk = vmdk_co_flush,
|
||||||
.bdrv_co_is_allocated = vmdk_co_is_allocated,
|
.bdrv_co_get_block_status = vmdk_co_get_block_status,
|
||||||
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
|
.bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
|
||||||
.bdrv_has_zero_init = vmdk_has_zero_init,
|
.bdrv_has_zero_init = vmdk_has_zero_init,
|
||||||
|
|
||||||
|
@ -2874,16 +2874,17 @@ static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int coroutine_fn vvfat_co_is_allocated(BlockDriverState *bs,
|
static int64_t coroutine_fn vvfat_co_get_block_status(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors, int* n)
|
int64_t sector_num, int nb_sectors, int* n)
|
||||||
{
|
{
|
||||||
BDRVVVFATState* s = bs->opaque;
|
BDRVVVFATState* s = bs->opaque;
|
||||||
*n = s->sector_count - sector_num;
|
*n = s->sector_count - sector_num;
|
||||||
if (*n > nb_sectors)
|
if (*n > nb_sectors) {
|
||||||
*n = nb_sectors;
|
*n = nb_sectors;
|
||||||
else if (*n < 0)
|
} else if (*n < 0) {
|
||||||
return 0;
|
return 0;
|
||||||
return 1;
|
}
|
||||||
|
return BDRV_BLOCK_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
|
static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
|
||||||
@ -2894,7 +2895,7 @@ static int write_target_commit(BlockDriverState *bs, int64_t sector_num,
|
|||||||
|
|
||||||
static void write_target_close(BlockDriverState *bs) {
|
static void write_target_close(BlockDriverState *bs) {
|
||||||
BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
|
BDRVVVFATState* s = *((BDRVVVFATState**) bs->opaque);
|
||||||
bdrv_delete(s->qcow);
|
bdrv_unref(s->qcow);
|
||||||
g_free(s->qcow_filename);
|
g_free(s->qcow_filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2935,7 +2936,7 @@ static int enable_write_target(BDRVVVFATState *s)
|
|||||||
ret = bdrv_open(s->qcow, s->qcow_filename, NULL,
|
ret = bdrv_open(s->qcow, s->qcow_filename, NULL,
|
||||||
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow);
|
BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH, bdrv_qcow);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_delete(s->qcow);
|
bdrv_unref(s->qcow);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2943,7 +2944,7 @@ static int enable_write_target(BDRVVVFATState *s)
|
|||||||
unlink(s->qcow_filename);
|
unlink(s->qcow_filename);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
s->bs->backing_hd = calloc(sizeof(BlockDriverState), 1);
|
s->bs->backing_hd = bdrv_new("");
|
||||||
s->bs->backing_hd->drv = &vvfat_write_target;
|
s->bs->backing_hd->drv = &vvfat_write_target;
|
||||||
s->bs->backing_hd->opaque = g_malloc(sizeof(void*));
|
s->bs->backing_hd->opaque = g_malloc(sizeof(void*));
|
||||||
*(void**)s->bs->backing_hd->opaque = s;
|
*(void**)s->bs->backing_hd->opaque = s;
|
||||||
@ -2984,7 +2985,7 @@ static BlockDriver bdrv_vvfat = {
|
|||||||
|
|
||||||
.bdrv_read = vvfat_co_read,
|
.bdrv_read = vvfat_co_read,
|
||||||
.bdrv_write = vvfat_co_write,
|
.bdrv_write = vvfat_co_write,
|
||||||
.bdrv_co_is_allocated = vvfat_co_is_allocated,
|
.bdrv_co_get_block_status = vvfat_co_get_block_status,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void bdrv_vvfat_init(void)
|
static void bdrv_vvfat_init(void)
|
||||||
|
@ -69,12 +69,6 @@ static void nbd_close_notifier(Notifier *n, void *data)
|
|||||||
g_free(cn);
|
g_free(cn);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nbd_server_put_ref(NBDExport *exp)
|
|
||||||
{
|
|
||||||
BlockDriverState *bs = nbd_export_get_blockdev(exp);
|
|
||||||
drive_put_ref(drive_get_by_blockdev(bs));
|
|
||||||
}
|
|
||||||
|
|
||||||
void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
@ -105,11 +99,9 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable,
|
|||||||
writable = false;
|
writable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY,
|
exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, NULL);
|
||||||
nbd_server_put_ref);
|
|
||||||
|
|
||||||
nbd_export_set_name(exp, device);
|
nbd_export_set_name(exp, device);
|
||||||
drive_get_ref(drive_get_by_blockdev(bs));
|
|
||||||
|
|
||||||
n = g_malloc0(sizeof(NBDCloseNotifier));
|
n = g_malloc0(sizeof(NBDCloseNotifier));
|
||||||
n->n.notify = nbd_close_notifier;
|
n->n.notify = nbd_close_notifier;
|
||||||
|
242
blockdev.c
242
blockdev.c
@ -212,7 +212,7 @@ static void bdrv_format_print(void *opaque, const char *name)
|
|||||||
static void drive_uninit(DriveInfo *dinfo)
|
static void drive_uninit(DriveInfo *dinfo)
|
||||||
{
|
{
|
||||||
qemu_opts_del(dinfo->opts);
|
qemu_opts_del(dinfo->opts);
|
||||||
bdrv_delete(dinfo->bdrv);
|
bdrv_unref(dinfo->bdrv);
|
||||||
g_free(dinfo->id);
|
g_free(dinfo->id);
|
||||||
QTAILQ_REMOVE(&drives, dinfo, next);
|
QTAILQ_REMOVE(&drives, dinfo, next);
|
||||||
g_free(dinfo->serial);
|
g_free(dinfo->serial);
|
||||||
@ -234,32 +234,32 @@ void drive_get_ref(DriveInfo *dinfo)
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
QEMUBH *bh;
|
QEMUBH *bh;
|
||||||
DriveInfo *dinfo;
|
BlockDriverState *bs;
|
||||||
} DrivePutRefBH;
|
} BDRVPutRefBH;
|
||||||
|
|
||||||
static void drive_put_ref_bh(void *opaque)
|
static void bdrv_put_ref_bh(void *opaque)
|
||||||
{
|
{
|
||||||
DrivePutRefBH *s = opaque;
|
BDRVPutRefBH *s = opaque;
|
||||||
|
|
||||||
drive_put_ref(s->dinfo);
|
bdrv_unref(s->bs);
|
||||||
qemu_bh_delete(s->bh);
|
qemu_bh_delete(s->bh);
|
||||||
g_free(s);
|
g_free(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Release a drive reference in a BH
|
* Release a BDS reference in a BH
|
||||||
*
|
*
|
||||||
* It is not possible to use drive_put_ref() from a callback function when the
|
* It is not safe to use bdrv_unref() from a callback function when the callers
|
||||||
* callers still need the drive. In such cases we schedule a BH to release the
|
* still need the BlockDriverState. In such cases we schedule a BH to release
|
||||||
* reference.
|
* the reference.
|
||||||
*/
|
*/
|
||||||
static void drive_put_ref_bh_schedule(DriveInfo *dinfo)
|
static void bdrv_put_ref_bh_schedule(BlockDriverState *bs)
|
||||||
{
|
{
|
||||||
DrivePutRefBH *s;
|
BDRVPutRefBH *s;
|
||||||
|
|
||||||
s = g_new(DrivePutRefBH, 1);
|
s = g_new(BDRVPutRefBH, 1);
|
||||||
s->bh = qemu_bh_new(drive_put_ref_bh, s);
|
s->bh = qemu_bh_new(bdrv_put_ref_bh, s);
|
||||||
s->dinfo = dinfo;
|
s->bs = bs;
|
||||||
qemu_bh_schedule(s->bh);
|
qemu_bh_schedule(s->bh);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,32 +280,16 @@ static int parse_block_error_action(const char *buf, bool is_read)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool do_check_io_limits(BlockIOLimit *io_limits, Error **errp)
|
static bool check_throttle_config(ThrottleConfig *cfg, Error **errp)
|
||||||
{
|
{
|
||||||
bool bps_flag;
|
if (throttle_conflicting(cfg)) {
|
||||||
bool iops_flag;
|
error_setg(errp, "bps/iops/max total values and read/write values"
|
||||||
|
" cannot be used at the same time");
|
||||||
assert(io_limits);
|
|
||||||
|
|
||||||
bps_flag = (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] != 0)
|
|
||||||
&& ((io_limits->bps[BLOCK_IO_LIMIT_READ] != 0)
|
|
||||||
|| (io_limits->bps[BLOCK_IO_LIMIT_WRITE] != 0));
|
|
||||||
iops_flag = (io_limits->iops[BLOCK_IO_LIMIT_TOTAL] != 0)
|
|
||||||
&& ((io_limits->iops[BLOCK_IO_LIMIT_READ] != 0)
|
|
||||||
|| (io_limits->iops[BLOCK_IO_LIMIT_WRITE] != 0));
|
|
||||||
if (bps_flag || iops_flag) {
|
|
||||||
error_setg(errp, "bps(iops) and bps_rd/bps_wr(iops_rd/iops_wr) "
|
|
||||||
"cannot be used at the same time");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (io_limits->bps[BLOCK_IO_LIMIT_TOTAL] < 0 ||
|
if (!throttle_is_valid(cfg)) {
|
||||||
io_limits->bps[BLOCK_IO_LIMIT_WRITE] < 0 ||
|
error_setg(errp, "bps/iops/maxs values must be 0 or greater");
|
||||||
io_limits->bps[BLOCK_IO_LIMIT_READ] < 0 ||
|
|
||||||
io_limits->iops[BLOCK_IO_LIMIT_TOTAL] < 0 ||
|
|
||||||
io_limits->iops[BLOCK_IO_LIMIT_WRITE] < 0 ||
|
|
||||||
io_limits->iops[BLOCK_IO_LIMIT_READ] < 0) {
|
|
||||||
error_setg(errp, "bps and iops values must be 0 or greater");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,7 +314,7 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||||||
int on_read_error, on_write_error;
|
int on_read_error, on_write_error;
|
||||||
const char *devaddr;
|
const char *devaddr;
|
||||||
DriveInfo *dinfo;
|
DriveInfo *dinfo;
|
||||||
BlockIOLimit io_limits;
|
ThrottleConfig cfg;
|
||||||
int snapshot = 0;
|
int snapshot = 0;
|
||||||
bool copy_on_read;
|
bool copy_on_read;
|
||||||
int ret;
|
int ret;
|
||||||
@ -496,20 +480,36 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* disk I/O throttling */
|
/* disk I/O throttling */
|
||||||
io_limits.bps[BLOCK_IO_LIMIT_TOTAL] =
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
cfg.buckets[THROTTLE_BPS_TOTAL].avg =
|
||||||
qemu_opt_get_number(opts, "throttling.bps-total", 0);
|
qemu_opt_get_number(opts, "throttling.bps-total", 0);
|
||||||
io_limits.bps[BLOCK_IO_LIMIT_READ] =
|
cfg.buckets[THROTTLE_BPS_READ].avg =
|
||||||
qemu_opt_get_number(opts, "throttling.bps-read", 0);
|
qemu_opt_get_number(opts, "throttling.bps-read", 0);
|
||||||
io_limits.bps[BLOCK_IO_LIMIT_WRITE] =
|
cfg.buckets[THROTTLE_BPS_WRITE].avg =
|
||||||
qemu_opt_get_number(opts, "throttling.bps-write", 0);
|
qemu_opt_get_number(opts, "throttling.bps-write", 0);
|
||||||
io_limits.iops[BLOCK_IO_LIMIT_TOTAL] =
|
cfg.buckets[THROTTLE_OPS_TOTAL].avg =
|
||||||
qemu_opt_get_number(opts, "throttling.iops-total", 0);
|
qemu_opt_get_number(opts, "throttling.iops-total", 0);
|
||||||
io_limits.iops[BLOCK_IO_LIMIT_READ] =
|
cfg.buckets[THROTTLE_OPS_READ].avg =
|
||||||
qemu_opt_get_number(opts, "throttling.iops-read", 0);
|
qemu_opt_get_number(opts, "throttling.iops-read", 0);
|
||||||
io_limits.iops[BLOCK_IO_LIMIT_WRITE] =
|
cfg.buckets[THROTTLE_OPS_WRITE].avg =
|
||||||
qemu_opt_get_number(opts, "throttling.iops-write", 0);
|
qemu_opt_get_number(opts, "throttling.iops-write", 0);
|
||||||
|
|
||||||
if (!do_check_io_limits(&io_limits, &error)) {
|
cfg.buckets[THROTTLE_BPS_TOTAL].max =
|
||||||
|
qemu_opt_get_number(opts, "throttling.bps-total-max", 0);
|
||||||
|
cfg.buckets[THROTTLE_BPS_READ].max =
|
||||||
|
qemu_opt_get_number(opts, "throttling.bps-read-max", 0);
|
||||||
|
cfg.buckets[THROTTLE_BPS_WRITE].max =
|
||||||
|
qemu_opt_get_number(opts, "throttling.bps-write-max", 0);
|
||||||
|
cfg.buckets[THROTTLE_OPS_TOTAL].max =
|
||||||
|
qemu_opt_get_number(opts, "throttling.iops-total-max", 0);
|
||||||
|
cfg.buckets[THROTTLE_OPS_READ].max =
|
||||||
|
qemu_opt_get_number(opts, "throttling.iops-read-max", 0);
|
||||||
|
cfg.buckets[THROTTLE_OPS_WRITE].max =
|
||||||
|
qemu_opt_get_number(opts, "throttling.iops-write-max", 0);
|
||||||
|
|
||||||
|
cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0);
|
||||||
|
|
||||||
|
if (!check_throttle_config(&cfg, &error)) {
|
||||||
error_report("%s", error_get_pretty(error));
|
error_report("%s", error_get_pretty(error));
|
||||||
error_free(error);
|
error_free(error);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -636,7 +636,10 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||||||
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
|
bdrv_set_on_error(dinfo->bdrv, on_read_error, on_write_error);
|
||||||
|
|
||||||
/* disk I/O throttling */
|
/* disk I/O throttling */
|
||||||
bdrv_set_io_limits(dinfo->bdrv, &io_limits);
|
if (throttle_enabled(&cfg)) {
|
||||||
|
bdrv_io_limits_enable(dinfo->bdrv);
|
||||||
|
bdrv_set_io_limits(dinfo->bdrv, &cfg);
|
||||||
|
}
|
||||||
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case IF_IDE:
|
case IF_IDE:
|
||||||
@ -732,7 +735,7 @@ static DriveInfo *blockdev_init(QemuOpts *all_opts,
|
|||||||
err:
|
err:
|
||||||
qemu_opts_del(opts);
|
qemu_opts_del(opts);
|
||||||
QDECREF(bs_opts);
|
QDECREF(bs_opts);
|
||||||
bdrv_delete(dinfo->bdrv);
|
bdrv_unref(dinfo->bdrv);
|
||||||
g_free(dinfo->id);
|
g_free(dinfo->id);
|
||||||
QTAILQ_REMOVE(&drives, dinfo, next);
|
QTAILQ_REMOVE(&drives, dinfo, next);
|
||||||
g_free(dinfo);
|
g_free(dinfo);
|
||||||
@ -763,6 +766,17 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type)
|
|||||||
qemu_opt_rename(all_opts, "bps_rd", "throttling.bps-read");
|
qemu_opt_rename(all_opts, "bps_rd", "throttling.bps-read");
|
||||||
qemu_opt_rename(all_opts, "bps_wr", "throttling.bps-write");
|
qemu_opt_rename(all_opts, "bps_wr", "throttling.bps-write");
|
||||||
|
|
||||||
|
qemu_opt_rename(all_opts, "iops_max", "throttling.iops-total-max");
|
||||||
|
qemu_opt_rename(all_opts, "iops_rd_max", "throttling.iops-read-max");
|
||||||
|
qemu_opt_rename(all_opts, "iops_wr_max", "throttling.iops-write-max");
|
||||||
|
|
||||||
|
qemu_opt_rename(all_opts, "bps_max", "throttling.bps-total-max");
|
||||||
|
qemu_opt_rename(all_opts, "bps_rd_max", "throttling.bps-read-max");
|
||||||
|
qemu_opt_rename(all_opts, "bps_wr_max", "throttling.bps-write-max");
|
||||||
|
|
||||||
|
qemu_opt_rename(all_opts,
|
||||||
|
"iops_size", "throttling.iops-size");
|
||||||
|
|
||||||
qemu_opt_rename(all_opts, "readonly", "read-only");
|
qemu_opt_rename(all_opts, "readonly", "read-only");
|
||||||
|
|
||||||
value = qemu_opt_get(all_opts, "cache");
|
value = qemu_opt_get(all_opts, "cache");
|
||||||
@ -982,7 +996,7 @@ static void external_snapshot_abort(BlkTransactionState *common)
|
|||||||
ExternalSnapshotState *state =
|
ExternalSnapshotState *state =
|
||||||
DO_UPCAST(ExternalSnapshotState, common, common);
|
DO_UPCAST(ExternalSnapshotState, common, common);
|
||||||
if (state->new_bs) {
|
if (state->new_bs) {
|
||||||
bdrv_delete(state->new_bs);
|
bdrv_unref(state->new_bs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1247,10 +1261,26 @@ void qmp_change_blockdev(const char *device, const char *filename,
|
|||||||
|
|
||||||
/* throttling disk I/O limits */
|
/* throttling disk I/O limits */
|
||||||
void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
||||||
int64_t bps_wr, int64_t iops, int64_t iops_rd,
|
int64_t bps_wr,
|
||||||
int64_t iops_wr, Error **errp)
|
int64_t iops,
|
||||||
|
int64_t iops_rd,
|
||||||
|
int64_t iops_wr,
|
||||||
|
bool has_bps_max,
|
||||||
|
int64_t bps_max,
|
||||||
|
bool has_bps_rd_max,
|
||||||
|
int64_t bps_rd_max,
|
||||||
|
bool has_bps_wr_max,
|
||||||
|
int64_t bps_wr_max,
|
||||||
|
bool has_iops_max,
|
||||||
|
int64_t iops_max,
|
||||||
|
bool has_iops_rd_max,
|
||||||
|
int64_t iops_rd_max,
|
||||||
|
bool has_iops_wr_max,
|
||||||
|
int64_t iops_wr_max,
|
||||||
|
bool has_iops_size,
|
||||||
|
int64_t iops_size, Error **errp)
|
||||||
{
|
{
|
||||||
BlockIOLimit io_limits;
|
ThrottleConfig cfg;
|
||||||
BlockDriverState *bs;
|
BlockDriverState *bs;
|
||||||
|
|
||||||
bs = bdrv_find(device);
|
bs = bdrv_find(device);
|
||||||
@ -1259,27 +1289,50 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
io_limits.bps[BLOCK_IO_LIMIT_TOTAL] = bps;
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
io_limits.bps[BLOCK_IO_LIMIT_READ] = bps_rd;
|
cfg.buckets[THROTTLE_BPS_TOTAL].avg = bps;
|
||||||
io_limits.bps[BLOCK_IO_LIMIT_WRITE] = bps_wr;
|
cfg.buckets[THROTTLE_BPS_READ].avg = bps_rd;
|
||||||
io_limits.iops[BLOCK_IO_LIMIT_TOTAL]= iops;
|
cfg.buckets[THROTTLE_BPS_WRITE].avg = bps_wr;
|
||||||
io_limits.iops[BLOCK_IO_LIMIT_READ] = iops_rd;
|
|
||||||
io_limits.iops[BLOCK_IO_LIMIT_WRITE]= iops_wr;
|
|
||||||
|
|
||||||
if (!do_check_io_limits(&io_limits, errp)) {
|
cfg.buckets[THROTTLE_OPS_TOTAL].avg = iops;
|
||||||
|
cfg.buckets[THROTTLE_OPS_READ].avg = iops_rd;
|
||||||
|
cfg.buckets[THROTTLE_OPS_WRITE].avg = iops_wr;
|
||||||
|
|
||||||
|
if (has_bps_max) {
|
||||||
|
cfg.buckets[THROTTLE_BPS_TOTAL].max = bps_max;
|
||||||
|
}
|
||||||
|
if (has_bps_rd_max) {
|
||||||
|
cfg.buckets[THROTTLE_BPS_READ].max = bps_rd_max;
|
||||||
|
}
|
||||||
|
if (has_bps_wr_max) {
|
||||||
|
cfg.buckets[THROTTLE_BPS_WRITE].max = bps_wr_max;
|
||||||
|
}
|
||||||
|
if (has_iops_max) {
|
||||||
|
cfg.buckets[THROTTLE_OPS_TOTAL].max = iops_max;
|
||||||
|
}
|
||||||
|
if (has_iops_rd_max) {
|
||||||
|
cfg.buckets[THROTTLE_OPS_READ].max = iops_rd_max;
|
||||||
|
}
|
||||||
|
if (has_iops_wr_max) {
|
||||||
|
cfg.buckets[THROTTLE_OPS_WRITE].max = iops_wr_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_iops_size) {
|
||||||
|
cfg.op_size = iops_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!check_throttle_config(&cfg, errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bs->io_limits = io_limits;
|
if (!bs->io_limits_enabled && throttle_enabled(&cfg)) {
|
||||||
|
|
||||||
if (!bs->io_limits_enabled && bdrv_io_limits_enabled(bs)) {
|
|
||||||
bdrv_io_limits_enable(bs);
|
bdrv_io_limits_enable(bs);
|
||||||
} else if (bs->io_limits_enabled && !bdrv_io_limits_enabled(bs)) {
|
} else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) {
|
||||||
bdrv_io_limits_disable(bs);
|
bdrv_io_limits_disable(bs);
|
||||||
} else {
|
}
|
||||||
if (bs->block_timer) {
|
|
||||||
timer_mod(bs->block_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL));
|
if (bs->io_limits_enabled) {
|
||||||
}
|
bdrv_set_io_limits(bs, &cfg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1383,7 +1436,7 @@ static void block_job_cb(void *opaque, int ret)
|
|||||||
}
|
}
|
||||||
qobject_decref(obj);
|
qobject_decref(obj);
|
||||||
|
|
||||||
drive_put_ref_bh_schedule(drive_get_by_blockdev(bs));
|
bdrv_put_ref_bh_schedule(bs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_block_stream(const char *device, bool has_base,
|
void qmp_block_stream(const char *device, bool has_base,
|
||||||
@ -1420,11 +1473,6 @@ void qmp_block_stream(const char *device, bool has_base,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Grab a reference so hotplug does not delete the BlockDriverState from
|
|
||||||
* underneath us.
|
|
||||||
*/
|
|
||||||
drive_get_ref(drive_get_by_blockdev(bs));
|
|
||||||
|
|
||||||
trace_qmp_block_stream(bs, bs->job);
|
trace_qmp_block_stream(bs, bs->job);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1481,10 +1529,6 @@ void qmp_block_commit(const char *device,
|
|||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Grab a reference so hotplug does not delete the BlockDriverState from
|
|
||||||
* underneath us.
|
|
||||||
*/
|
|
||||||
drive_get_ref(drive_get_by_blockdev(bs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_drive_backup(const char *device, const char *target,
|
void qmp_drive_backup(const char *device, const char *target,
|
||||||
@ -1585,7 +1629,7 @@ void qmp_drive_backup(const char *device, const char *target,
|
|||||||
target_bs = bdrv_new("");
|
target_bs = bdrv_new("");
|
||||||
ret = bdrv_open(target_bs, target, NULL, flags, drv);
|
ret = bdrv_open(target_bs, target, NULL, flags, drv);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_delete(target_bs);
|
bdrv_unref(target_bs);
|
||||||
error_setg_file_open(errp, -ret, target);
|
error_setg_file_open(errp, -ret, target);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1593,15 +1637,10 @@ void qmp_drive_backup(const char *device, const char *target,
|
|||||||
backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
|
backup_start(bs, target_bs, speed, sync, on_source_error, on_target_error,
|
||||||
block_job_cb, bs, &local_err);
|
block_job_cb, bs, &local_err);
|
||||||
if (local_err != NULL) {
|
if (local_err != NULL) {
|
||||||
bdrv_delete(target_bs);
|
bdrv_unref(target_bs);
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Grab a reference so hotplug does not delete the BlockDriverState from
|
|
||||||
* underneath us.
|
|
||||||
*/
|
|
||||||
drive_get_ref(drive_get_by_blockdev(bs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
|
#define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
|
||||||
@ -1725,7 +1764,7 @@ void qmp_drive_mirror(const char *device, const char *target,
|
|||||||
target_bs = bdrv_new("");
|
target_bs = bdrv_new("");
|
||||||
ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv);
|
ret = bdrv_open(target_bs, target, NULL, flags | BDRV_O_NO_BACKING, drv);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_delete(target_bs);
|
bdrv_unref(target_bs);
|
||||||
error_setg_file_open(errp, -ret, target);
|
error_setg_file_open(errp, -ret, target);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1734,15 +1773,10 @@ void qmp_drive_mirror(const char *device, const char *target,
|
|||||||
on_source_error, on_target_error,
|
on_source_error, on_target_error,
|
||||||
block_job_cb, bs, &local_err);
|
block_job_cb, bs, &local_err);
|
||||||
if (local_err != NULL) {
|
if (local_err != NULL) {
|
||||||
bdrv_delete(target_bs);
|
bdrv_unref(target_bs);
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Grab a reference so hotplug does not delete the BlockDriverState from
|
|
||||||
* underneath us.
|
|
||||||
*/
|
|
||||||
drive_get_ref(drive_get_by_blockdev(bs));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static BlockJob *find_block_job(const char *device)
|
static BlockJob *find_block_job(const char *device)
|
||||||
@ -1967,6 +2001,34 @@ QemuOptsList qemu_common_drive_opts = {
|
|||||||
.name = "throttling.bps-write",
|
.name = "throttling.bps-write",
|
||||||
.type = QEMU_OPT_NUMBER,
|
.type = QEMU_OPT_NUMBER,
|
||||||
.help = "limit write bytes per second",
|
.help = "limit write bytes per second",
|
||||||
|
},{
|
||||||
|
.name = "throttling.iops-total-max",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "I/O operations burst",
|
||||||
|
},{
|
||||||
|
.name = "throttling.iops-read-max",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "I/O operations read burst",
|
||||||
|
},{
|
||||||
|
.name = "throttling.iops-write-max",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "I/O operations write burst",
|
||||||
|
},{
|
||||||
|
.name = "throttling.bps-total-max",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "total bytes burst",
|
||||||
|
},{
|
||||||
|
.name = "throttling.bps-read-max",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "total bytes read burst",
|
||||||
|
},{
|
||||||
|
.name = "throttling.bps-write-max",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "total bytes write burst",
|
||||||
|
},{
|
||||||
|
.name = "throttling.iops-size",
|
||||||
|
.type = QEMU_OPT_NUMBER,
|
||||||
|
.help = "when limiting by iops max size of an I/O in bytes",
|
||||||
},{
|
},{
|
||||||
.name = "copy-on-read",
|
.name = "copy-on-read",
|
||||||
.type = QEMU_OPT_BOOL,
|
.type = QEMU_OPT_BOOL,
|
||||||
|
@ -45,6 +45,7 @@ void *block_job_create(const BlockJobType *job_type, BlockDriverState *bs,
|
|||||||
error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
|
error_set(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
bdrv_ref(bs);
|
||||||
bdrv_set_in_use(bs, 1);
|
bdrv_set_in_use(bs, 1);
|
||||||
|
|
||||||
job = g_malloc0(job_type->instance_size);
|
job = g_malloc0(job_type->instance_size);
|
||||||
|
36
hmp.c
36
hmp.c
@ -344,14 +344,30 @@ void hmp_info_block(Monitor *mon, const QDict *qdict)
|
|||||||
{
|
{
|
||||||
monitor_printf(mon, " I/O throttling: bps=%" PRId64
|
monitor_printf(mon, " I/O throttling: bps=%" PRId64
|
||||||
" bps_rd=%" PRId64 " bps_wr=%" PRId64
|
" bps_rd=%" PRId64 " bps_wr=%" PRId64
|
||||||
|
" bps_max=%" PRId64
|
||||||
|
" bps_rd_max=%" PRId64
|
||||||
|
" bps_wr_max=%" PRId64
|
||||||
" iops=%" PRId64 " iops_rd=%" PRId64
|
" iops=%" PRId64 " iops_rd=%" PRId64
|
||||||
" iops_wr=%" PRId64 "\n",
|
" iops_wr=%" PRId64
|
||||||
|
" iops_max=%" PRId64
|
||||||
|
" iops_rd_max=%" PRId64
|
||||||
|
" iops_wr_max=%" PRId64
|
||||||
|
" iops_size=%" PRId64 "\n",
|
||||||
info->value->inserted->bps,
|
info->value->inserted->bps,
|
||||||
info->value->inserted->bps_rd,
|
info->value->inserted->bps_rd,
|
||||||
info->value->inserted->bps_wr,
|
info->value->inserted->bps_wr,
|
||||||
|
info->value->inserted->bps_max,
|
||||||
|
info->value->inserted->bps_rd_max,
|
||||||
|
info->value->inserted->bps_wr_max,
|
||||||
info->value->inserted->iops,
|
info->value->inserted->iops,
|
||||||
info->value->inserted->iops_rd,
|
info->value->inserted->iops_rd,
|
||||||
info->value->inserted->iops_wr);
|
info->value->inserted->iops_wr,
|
||||||
|
info->value->inserted->iops_max,
|
||||||
|
info->value->inserted->iops_rd_max,
|
||||||
|
info->value->inserted->iops_wr_max,
|
||||||
|
info->value->inserted->iops_size);
|
||||||
|
} else {
|
||||||
|
monitor_printf(mon, " [not inserted]");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
@ -1098,7 +1114,21 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict)
|
|||||||
qdict_get_int(qdict, "bps_wr"),
|
qdict_get_int(qdict, "bps_wr"),
|
||||||
qdict_get_int(qdict, "iops"),
|
qdict_get_int(qdict, "iops"),
|
||||||
qdict_get_int(qdict, "iops_rd"),
|
qdict_get_int(qdict, "iops_rd"),
|
||||||
qdict_get_int(qdict, "iops_wr"), &err);
|
qdict_get_int(qdict, "iops_wr"),
|
||||||
|
false, /* no burst max via HMP */
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
false, /* No default I/O size */
|
||||||
|
0, &err);
|
||||||
hmp_handle_error(mon, &err);
|
hmp_handle_error(mon, &err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ typedef struct {
|
|||||||
|
|
||||||
struct VirtIOBlockDataPlane {
|
struct VirtIOBlockDataPlane {
|
||||||
bool started;
|
bool started;
|
||||||
|
bool starting;
|
||||||
bool stopping;
|
bool stopping;
|
||||||
QEMUBH *start_bh;
|
QEMUBH *start_bh;
|
||||||
QemuThread thread;
|
QemuThread thread;
|
||||||
@ -451,8 +452,15 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (s->starting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->starting = true;
|
||||||
|
|
||||||
vq = virtio_get_queue(s->vdev, 0);
|
vq = virtio_get_queue(s->vdev, 0);
|
||||||
if (!vring_setup(&s->vring, s->vdev, 0)) {
|
if (!vring_setup(&s->vring, s->vdev, 0)) {
|
||||||
|
s->starting = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,6 +490,7 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
|
|||||||
s->io_notifier = *ioq_get_notifier(&s->ioqueue);
|
s->io_notifier = *ioq_get_notifier(&s->ioqueue);
|
||||||
aio_set_event_notifier(s->ctx, &s->io_notifier, handle_io);
|
aio_set_event_notifier(s->ctx, &s->io_notifier, handle_io);
|
||||||
|
|
||||||
|
s->starting = false;
|
||||||
s->started = true;
|
s->started = true;
|
||||||
trace_virtio_blk_data_plane_start(s);
|
trace_virtio_blk_data_plane_start(s);
|
||||||
|
|
||||||
|
@ -813,7 +813,7 @@ static int blk_connect(struct XenDevice *xendev)
|
|||||||
readonly);
|
readonly);
|
||||||
if (bdrv_open(blkdev->bs,
|
if (bdrv_open(blkdev->bs,
|
||||||
blkdev->filename, NULL, qflags, drv) != 0) {
|
blkdev->filename, NULL, qflags, drv) != 0) {
|
||||||
bdrv_delete(blkdev->bs);
|
bdrv_unref(blkdev->bs);
|
||||||
blkdev->bs = NULL;
|
blkdev->bs = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -824,6 +824,9 @@ static int blk_connect(struct XenDevice *xendev)
|
|||||||
/* setup via qemu cmdline -> already setup for us */
|
/* setup via qemu cmdline -> already setup for us */
|
||||||
xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
|
xen_be_printf(&blkdev->xendev, 2, "get configured bdrv (cmdline setup)\n");
|
||||||
blkdev->bs = blkdev->dinfo->bdrv;
|
blkdev->bs = blkdev->dinfo->bdrv;
|
||||||
|
/* blkdev->bs is not create by us, we get a reference
|
||||||
|
* so we can bdrv_unref() unconditionally */
|
||||||
|
bdrv_ref(blkdev->bs);
|
||||||
}
|
}
|
||||||
bdrv_attach_dev_nofail(blkdev->bs, blkdev);
|
bdrv_attach_dev_nofail(blkdev->bs, blkdev);
|
||||||
blkdev->file_size = bdrv_getlength(blkdev->bs);
|
blkdev->file_size = bdrv_getlength(blkdev->bs);
|
||||||
@ -922,12 +925,8 @@ static void blk_disconnect(struct XenDevice *xendev)
|
|||||||
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
|
struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev);
|
||||||
|
|
||||||
if (blkdev->bs) {
|
if (blkdev->bs) {
|
||||||
if (!blkdev->dinfo) {
|
bdrv_detach_dev(blkdev->bs, blkdev);
|
||||||
/* close/delete only if we created it ourself */
|
bdrv_unref(blkdev->bs);
|
||||||
bdrv_close(blkdev->bs);
|
|
||||||
bdrv_detach_dev(blkdev->bs, blkdev);
|
|
||||||
bdrv_delete(blkdev->bs);
|
|
||||||
}
|
|
||||||
blkdev->bs = NULL;
|
blkdev->bs = NULL;
|
||||||
}
|
}
|
||||||
xen_be_unbind_evtchn(&blkdev->xendev);
|
xen_be_unbind_evtchn(&blkdev->xendev);
|
||||||
|
@ -81,6 +81,32 @@ typedef struct BlockDevOps {
|
|||||||
#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS)
|
#define BDRV_SECTOR_SIZE (1ULL << BDRV_SECTOR_BITS)
|
||||||
#define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1)
|
#define BDRV_SECTOR_MASK ~(BDRV_SECTOR_SIZE - 1)
|
||||||
|
|
||||||
|
/* BDRV_BLOCK_DATA: data is read from bs->file or another file
|
||||||
|
* BDRV_BLOCK_ZERO: sectors read as zero
|
||||||
|
* BDRV_BLOCK_OFFSET_VALID: sector stored in bs->file as raw data
|
||||||
|
*
|
||||||
|
* If BDRV_BLOCK_OFFSET_VALID is set, bits 9-62 represent the offset in
|
||||||
|
* bs->file where sector data can be read from as raw data.
|
||||||
|
*
|
||||||
|
* DATA == 0 && ZERO == 0 means that data is read from backing_hd if present.
|
||||||
|
*
|
||||||
|
* DATA ZERO OFFSET_VALID
|
||||||
|
* t t t sectors read as zero, bs->file is zero at offset
|
||||||
|
* t f t sectors read as valid from bs->file at offset
|
||||||
|
* f t t sectors preallocated, read as zero, bs->file not
|
||||||
|
* necessarily zero at offset
|
||||||
|
* f f t sectors preallocated but read from backing_hd,
|
||||||
|
* bs->file contains garbage at offset
|
||||||
|
* t t f sectors preallocated, read as zero, unknown offset
|
||||||
|
* t f f sectors read from unknown file or offset
|
||||||
|
* f t f not allocated or unknown offset, read as zero
|
||||||
|
* f f f not allocated or unknown offset, read from backing_hd
|
||||||
|
*/
|
||||||
|
#define BDRV_BLOCK_DATA 1
|
||||||
|
#define BDRV_BLOCK_ZERO 2
|
||||||
|
#define BDRV_BLOCK_OFFSET_VALID 4
|
||||||
|
#define BDRV_BLOCK_OFFSET_MASK BDRV_SECTOR_MASK
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP
|
BDRV_ACTION_REPORT, BDRV_ACTION_IGNORE, BDRV_ACTION_STOP
|
||||||
} BlockErrorAction;
|
} BlockErrorAction;
|
||||||
@ -107,7 +133,6 @@ void bdrv_info_stats(Monitor *mon, QObject **ret_data);
|
|||||||
/* disk I/O throttling */
|
/* disk I/O throttling */
|
||||||
void bdrv_io_limits_enable(BlockDriverState *bs);
|
void bdrv_io_limits_enable(BlockDriverState *bs);
|
||||||
void bdrv_io_limits_disable(BlockDriverState *bs);
|
void bdrv_io_limits_disable(BlockDriverState *bs);
|
||||||
bool bdrv_io_limits_enabled(BlockDriverState *bs);
|
|
||||||
|
|
||||||
void bdrv_init(void);
|
void bdrv_init(void);
|
||||||
void bdrv_init_with_whitelist(void);
|
void bdrv_init_with_whitelist(void);
|
||||||
@ -123,7 +148,6 @@ BlockDriverState *bdrv_new(const char *device_name);
|
|||||||
void bdrv_make_anon(BlockDriverState *bs);
|
void bdrv_make_anon(BlockDriverState *bs);
|
||||||
void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old);
|
void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old);
|
||||||
void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
|
void bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top);
|
||||||
void bdrv_delete(BlockDriverState *bs);
|
|
||||||
int bdrv_parse_cache_flags(const char *mode, int *flags);
|
int bdrv_parse_cache_flags(const char *mode, int *flags);
|
||||||
int bdrv_parse_discard_flags(const char *mode, int *flags);
|
int bdrv_parse_discard_flags(const char *mode, int *flags);
|
||||||
int bdrv_file_open(BlockDriverState **pbs, const char *filename,
|
int bdrv_file_open(BlockDriverState **pbs, const char *filename,
|
||||||
@ -181,12 +205,6 @@ int coroutine_fn bdrv_co_writev(BlockDriverState *bs, int64_t sector_num,
|
|||||||
*/
|
*/
|
||||||
int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
|
int coroutine_fn bdrv_co_write_zeroes(BlockDriverState *bs, int64_t sector_num,
|
||||||
int nb_sectors);
|
int nb_sectors);
|
||||||
int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t sector_num,
|
|
||||||
int nb_sectors, int *pnum);
|
|
||||||
int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *top,
|
|
||||||
BlockDriverState *base,
|
|
||||||
int64_t sector_num,
|
|
||||||
int nb_sectors, int *pnum);
|
|
||||||
BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
|
BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
|
||||||
const char *backing_file);
|
const char *backing_file);
|
||||||
int bdrv_get_backing_file_depth(BlockDriverState *bs);
|
int bdrv_get_backing_file_depth(BlockDriverState *bs);
|
||||||
@ -277,6 +295,8 @@ int bdrv_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
|
|||||||
int bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
|
int bdrv_co_discard(BlockDriverState *bs, int64_t sector_num, int nb_sectors);
|
||||||
int bdrv_has_zero_init_1(BlockDriverState *bs);
|
int bdrv_has_zero_init_1(BlockDriverState *bs);
|
||||||
int bdrv_has_zero_init(BlockDriverState *bs);
|
int bdrv_has_zero_init(BlockDriverState *bs);
|
||||||
|
int64_t bdrv_get_block_status(BlockDriverState *bs, int64_t sector_num,
|
||||||
|
int nb_sectors, int *pnum);
|
||||||
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
int bdrv_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors,
|
||||||
int *pnum);
|
int *pnum);
|
||||||
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
|
int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
|
||||||
@ -356,6 +376,8 @@ int64_t bdrv_get_dirty_count(BlockDriverState *bs);
|
|||||||
void bdrv_enable_copy_on_read(BlockDriverState *bs);
|
void bdrv_enable_copy_on_read(BlockDriverState *bs);
|
||||||
void bdrv_disable_copy_on_read(BlockDriverState *bs);
|
void bdrv_disable_copy_on_read(BlockDriverState *bs);
|
||||||
|
|
||||||
|
void bdrv_ref(BlockDriverState *bs);
|
||||||
|
void bdrv_unref(BlockDriverState *bs);
|
||||||
void bdrv_set_in_use(BlockDriverState *bs, int in_use);
|
void bdrv_set_in_use(BlockDriverState *bs, int in_use);
|
||||||
int bdrv_in_use(BlockDriverState *bs);
|
int bdrv_in_use(BlockDriverState *bs);
|
||||||
|
|
||||||
|
@ -35,18 +35,12 @@
|
|||||||
#include "qemu/hbitmap.h"
|
#include "qemu/hbitmap.h"
|
||||||
#include "block/snapshot.h"
|
#include "block/snapshot.h"
|
||||||
#include "qemu/main-loop.h"
|
#include "qemu/main-loop.h"
|
||||||
|
#include "qemu/throttle.h"
|
||||||
|
|
||||||
#define BLOCK_FLAG_ENCRYPT 1
|
#define BLOCK_FLAG_ENCRYPT 1
|
||||||
#define BLOCK_FLAG_COMPAT6 4
|
#define BLOCK_FLAG_COMPAT6 4
|
||||||
#define BLOCK_FLAG_LAZY_REFCOUNTS 8
|
#define BLOCK_FLAG_LAZY_REFCOUNTS 8
|
||||||
|
|
||||||
#define BLOCK_IO_LIMIT_READ 0
|
|
||||||
#define BLOCK_IO_LIMIT_WRITE 1
|
|
||||||
#define BLOCK_IO_LIMIT_TOTAL 2
|
|
||||||
|
|
||||||
#define BLOCK_IO_SLICE_TIME 100000000
|
|
||||||
#define NANOSECONDS_PER_SECOND 1000000000.0
|
|
||||||
|
|
||||||
#define BLOCK_OPT_SIZE "size"
|
#define BLOCK_OPT_SIZE "size"
|
||||||
#define BLOCK_OPT_ENCRYPT "encryption"
|
#define BLOCK_OPT_ENCRYPT "encryption"
|
||||||
#define BLOCK_OPT_COMPAT6 "compat6"
|
#define BLOCK_OPT_COMPAT6 "compat6"
|
||||||
@ -70,17 +64,6 @@ typedef struct BdrvTrackedRequest {
|
|||||||
CoQueue wait_queue; /* coroutines blocked on this request */
|
CoQueue wait_queue; /* coroutines blocked on this request */
|
||||||
} BdrvTrackedRequest;
|
} BdrvTrackedRequest;
|
||||||
|
|
||||||
|
|
||||||
typedef struct BlockIOLimit {
|
|
||||||
int64_t bps[3];
|
|
||||||
int64_t iops[3];
|
|
||||||
} BlockIOLimit;
|
|
||||||
|
|
||||||
typedef struct BlockIOBaseValue {
|
|
||||||
uint64_t bytes[2];
|
|
||||||
uint64_t ios[2];
|
|
||||||
} BlockIOBaseValue;
|
|
||||||
|
|
||||||
struct BlockDriver {
|
struct BlockDriver {
|
||||||
const char *format_name;
|
const char *format_name;
|
||||||
int instance_size;
|
int instance_size;
|
||||||
@ -135,7 +118,7 @@ struct BlockDriver {
|
|||||||
int64_t sector_num, int nb_sectors);
|
int64_t sector_num, int nb_sectors);
|
||||||
int coroutine_fn (*bdrv_co_discard)(BlockDriverState *bs,
|
int coroutine_fn (*bdrv_co_discard)(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors);
|
int64_t sector_num, int nb_sectors);
|
||||||
int coroutine_fn (*bdrv_co_is_allocated)(BlockDriverState *bs,
|
int64_t coroutine_fn (*bdrv_co_get_block_status)(BlockDriverState *bs,
|
||||||
int64_t sector_num, int nb_sectors, int *pnum);
|
int64_t sector_num, int nb_sectors, int *pnum);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -264,13 +247,9 @@ struct BlockDriverState {
|
|||||||
/* number of in-flight copy-on-read requests */
|
/* number of in-flight copy-on-read requests */
|
||||||
unsigned int copy_on_read_in_flight;
|
unsigned int copy_on_read_in_flight;
|
||||||
|
|
||||||
/* the time for latest disk I/O */
|
/* I/O throttling */
|
||||||
int64_t slice_start;
|
ThrottleState throttle_state;
|
||||||
int64_t slice_end;
|
CoQueue throttled_reqs[2];
|
||||||
BlockIOLimit io_limits;
|
|
||||||
BlockIOBaseValue slice_submitted;
|
|
||||||
CoQueue throttled_reqs;
|
|
||||||
QEMUTimer *block_timer;
|
|
||||||
bool io_limits_enabled;
|
bool io_limits_enabled;
|
||||||
|
|
||||||
/* I/O stats (display with "info blockstats"). */
|
/* I/O stats (display with "info blockstats"). */
|
||||||
@ -298,6 +277,7 @@ struct BlockDriverState {
|
|||||||
BlockDeviceIoStatus iostatus;
|
BlockDeviceIoStatus iostatus;
|
||||||
char device_name[32];
|
char device_name[32];
|
||||||
HBitmap *dirty_bitmap;
|
HBitmap *dirty_bitmap;
|
||||||
|
int refcnt;
|
||||||
int in_use; /* users other than guest access, eg. block migration */
|
int in_use; /* users other than guest access, eg. block migration */
|
||||||
QTAILQ_ENTRY(BlockDriverState) list;
|
QTAILQ_ENTRY(BlockDriverState) list;
|
||||||
|
|
||||||
@ -312,7 +292,8 @@ struct BlockDriverState {
|
|||||||
int get_tmp_filename(char *filename, int size);
|
int get_tmp_filename(char *filename, int size);
|
||||||
|
|
||||||
void bdrv_set_io_limits(BlockDriverState *bs,
|
void bdrv_set_io_limits(BlockDriverState *bs,
|
||||||
BlockIOLimit *io_limits);
|
ThrottleConfig *cfg);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bdrv_add_before_write_notifier:
|
* bdrv_add_before_write_notifier:
|
||||||
|
110
include/qemu/throttle.h
Normal file
110
include/qemu/throttle.h
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* QEMU throttling infrastructure
|
||||||
|
*
|
||||||
|
* Copyright (C) Nodalink, SARL. 2013
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* Benoît Canet <benoit.canet@irqsave.net>
|
||||||
|
*
|
||||||
|
* 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 or
|
||||||
|
* (at your option) version 3 of the License.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef THROTTLE_H
|
||||||
|
#define THROTTLE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "qemu-common.h"
|
||||||
|
#include "qemu/timer.h"
|
||||||
|
|
||||||
|
#define NANOSECONDS_PER_SECOND 1000000000.0
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
THROTTLE_BPS_TOTAL,
|
||||||
|
THROTTLE_BPS_READ,
|
||||||
|
THROTTLE_BPS_WRITE,
|
||||||
|
THROTTLE_OPS_TOTAL,
|
||||||
|
THROTTLE_OPS_READ,
|
||||||
|
THROTTLE_OPS_WRITE,
|
||||||
|
BUCKETS_COUNT,
|
||||||
|
} BucketType;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The max parameter of the leaky bucket throttling algorithm can be used to
|
||||||
|
* allow the guest to do bursts.
|
||||||
|
* The max value is a pool of I/O that the guest can use without being throttled
|
||||||
|
* at all. Throttling is triggered once this pool is empty.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct LeakyBucket {
|
||||||
|
double avg; /* average goal in units per second */
|
||||||
|
double max; /* leaky bucket max burst in units */
|
||||||
|
double level; /* bucket level in units */
|
||||||
|
} LeakyBucket;
|
||||||
|
|
||||||
|
/* The following structure is used to configure a ThrottleState
|
||||||
|
* It contains a bit of state: the bucket field of the LeakyBucket structure.
|
||||||
|
* However it allows to keep the code clean and the bucket field is reset to
|
||||||
|
* zero at the right time.
|
||||||
|
*/
|
||||||
|
typedef struct ThrottleConfig {
|
||||||
|
LeakyBucket buckets[BUCKETS_COUNT]; /* leaky buckets */
|
||||||
|
uint64_t op_size; /* size of an operation in bytes */
|
||||||
|
} ThrottleConfig;
|
||||||
|
|
||||||
|
typedef struct ThrottleState {
|
||||||
|
ThrottleConfig cfg; /* configuration */
|
||||||
|
int64_t previous_leak; /* timestamp of the last leak done */
|
||||||
|
QEMUTimer * timers[2]; /* timers used to do the throttling */
|
||||||
|
QEMUClockType clock_type; /* the clock used */
|
||||||
|
} ThrottleState;
|
||||||
|
|
||||||
|
/* operations on single leaky buckets */
|
||||||
|
void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta);
|
||||||
|
|
||||||
|
int64_t throttle_compute_wait(LeakyBucket *bkt);
|
||||||
|
|
||||||
|
/* expose timer computation function for unit tests */
|
||||||
|
bool throttle_compute_timer(ThrottleState *ts,
|
||||||
|
bool is_write,
|
||||||
|
int64_t now,
|
||||||
|
int64_t *next_timestamp);
|
||||||
|
|
||||||
|
/* init/destroy cycle */
|
||||||
|
void throttle_init(ThrottleState *ts,
|
||||||
|
QEMUClockType clock_type,
|
||||||
|
void (read_timer)(void *),
|
||||||
|
void (write_timer)(void *),
|
||||||
|
void *timer_opaque);
|
||||||
|
|
||||||
|
void throttle_destroy(ThrottleState *ts);
|
||||||
|
|
||||||
|
bool throttle_have_timer(ThrottleState *ts);
|
||||||
|
|
||||||
|
/* configuration */
|
||||||
|
bool throttle_enabled(ThrottleConfig *cfg);
|
||||||
|
|
||||||
|
bool throttle_conflicting(ThrottleConfig *cfg);
|
||||||
|
|
||||||
|
bool throttle_is_valid(ThrottleConfig *cfg);
|
||||||
|
|
||||||
|
void throttle_config(ThrottleState *ts, ThrottleConfig *cfg);
|
||||||
|
|
||||||
|
void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg);
|
||||||
|
|
||||||
|
/* usage */
|
||||||
|
bool throttle_schedule_timer(ThrottleState *ts, bool is_write);
|
||||||
|
|
||||||
|
void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
|
||||||
|
|
||||||
|
#endif
|
5
nbd.c
5
nbd.c
@ -882,6 +882,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset,
|
|||||||
exp->nbdflags = nbdflags;
|
exp->nbdflags = nbdflags;
|
||||||
exp->size = size == -1 ? bdrv_getlength(bs) : size;
|
exp->size = size == -1 ? bdrv_getlength(bs) : size;
|
||||||
exp->close = close;
|
exp->close = close;
|
||||||
|
bdrv_ref(bs);
|
||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -928,6 +929,10 @@ void nbd_export_close(NBDExport *exp)
|
|||||||
}
|
}
|
||||||
nbd_export_set_name(exp, NULL);
|
nbd_export_set_name(exp, NULL);
|
||||||
nbd_export_put(exp);
|
nbd_export_put(exp);
|
||||||
|
if (exp->bs) {
|
||||||
|
bdrv_unref(exp->bs);
|
||||||
|
exp->bs = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nbd_export_get(NBDExport *exp)
|
void nbd_export_get(NBDExport *exp)
|
||||||
|
@ -785,6 +785,20 @@
|
|||||||
#
|
#
|
||||||
# @image: the info of image used (since: 1.6)
|
# @image: the info of image used (since: 1.6)
|
||||||
#
|
#
|
||||||
|
# @bps_max: #optional total max in bytes (Since 1.7)
|
||||||
|
#
|
||||||
|
# @bps_rd_max: #optional read max in bytes (Since 1.7)
|
||||||
|
#
|
||||||
|
# @bps_wr_max: #optional write max in bytes (Since 1.7)
|
||||||
|
#
|
||||||
|
# @iops_max: #optional total I/O operations max (Since 1.7)
|
||||||
|
#
|
||||||
|
# @iops_rd_max: #optional read I/O operations max (Since 1.7)
|
||||||
|
#
|
||||||
|
# @iops_wr_max: #optional write I/O operations max (Since 1.7)
|
||||||
|
#
|
||||||
|
# @iops_size: #optional an I/O size in bytes (Since 1.7)
|
||||||
|
#
|
||||||
# Since: 0.14.0
|
# Since: 0.14.0
|
||||||
#
|
#
|
||||||
# Notes: This interface is only found in @BlockInfo.
|
# Notes: This interface is only found in @BlockInfo.
|
||||||
@ -795,7 +809,11 @@
|
|||||||
'encrypted': 'bool', 'encryption_key_missing': 'bool',
|
'encrypted': 'bool', 'encryption_key_missing': 'bool',
|
||||||
'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
|
'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
|
||||||
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
|
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
|
||||||
'image': 'ImageInfo' } }
|
'image': 'ImageInfo',
|
||||||
|
'*bps_max': 'int', '*bps_rd_max': 'int',
|
||||||
|
'*bps_wr_max': 'int', '*iops_max': 'int',
|
||||||
|
'*iops_rd_max': 'int', '*iops_wr_max': 'int',
|
||||||
|
'*iops_size': 'int' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockDeviceIoStatus:
|
# @BlockDeviceIoStatus:
|
||||||
@ -812,6 +830,35 @@
|
|||||||
##
|
##
|
||||||
{ 'enum': 'BlockDeviceIoStatus', 'data': [ 'ok', 'failed', 'nospace' ] }
|
{ 'enum': 'BlockDeviceIoStatus', 'data': [ 'ok', 'failed', 'nospace' ] }
|
||||||
|
|
||||||
|
##
|
||||||
|
# @BlockDeviceMapEntry:
|
||||||
|
#
|
||||||
|
# Entry in the metadata map of the device (returned by "qemu-img map")
|
||||||
|
#
|
||||||
|
# @start: Offset in the image of the first byte described by this entry
|
||||||
|
# (in bytes)
|
||||||
|
#
|
||||||
|
# @length: Length of the range described by this entry (in bytes)
|
||||||
|
#
|
||||||
|
# @depth: Number of layers (0 = top image, 1 = top image's backing file, etc.)
|
||||||
|
# before reaching one for which the range is allocated. The value is
|
||||||
|
# in the range 0 to the depth of the image chain - 1.
|
||||||
|
#
|
||||||
|
# @zero: the sectors in this range read as zeros
|
||||||
|
#
|
||||||
|
# @data: reading the image will actually read data from a file (in particular,
|
||||||
|
# if @offset is present this means that the sectors are not simply
|
||||||
|
# preallocated, but contain actual data in raw format)
|
||||||
|
#
|
||||||
|
# @offset: if present, the image file stores the data for this range in
|
||||||
|
# raw format at the given offset.
|
||||||
|
#
|
||||||
|
# Since 1.7
|
||||||
|
##
|
||||||
|
{ 'type': 'BlockDeviceMapEntry',
|
||||||
|
'data': { 'start': 'int', 'length': 'int', 'depth': 'int', 'zero': 'bool',
|
||||||
|
'data': 'bool', '*offset': 'int' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockDirtyInfo:
|
# @BlockDirtyInfo:
|
||||||
#
|
#
|
||||||
@ -2174,6 +2221,20 @@
|
|||||||
#
|
#
|
||||||
# @iops_wr: write I/O operations per second
|
# @iops_wr: write I/O operations per second
|
||||||
#
|
#
|
||||||
|
# @bps_max: #optional total max in bytes (Since 1.7)
|
||||||
|
#
|
||||||
|
# @bps_rd_max: #optional read max in bytes (Since 1.7)
|
||||||
|
#
|
||||||
|
# @bps_wr_max: #optional write max in bytes (Since 1.7)
|
||||||
|
#
|
||||||
|
# @iops_max: #optional total I/O operations max (Since 1.7)
|
||||||
|
#
|
||||||
|
# @iops_rd_max: #optional read I/O operations max (Since 1.7)
|
||||||
|
#
|
||||||
|
# @iops_wr_max: #optional write I/O operations max (Since 1.7)
|
||||||
|
#
|
||||||
|
# @iops_size: #optional an I/O size in bytes (Since 1.7)
|
||||||
|
#
|
||||||
# Returns: Nothing on success
|
# Returns: Nothing on success
|
||||||
# If @device is not a valid block device, DeviceNotFound
|
# If @device is not a valid block device, DeviceNotFound
|
||||||
#
|
#
|
||||||
@ -2181,7 +2242,11 @@
|
|||||||
##
|
##
|
||||||
{ 'command': 'block_set_io_throttle',
|
{ 'command': 'block_set_io_throttle',
|
||||||
'data': { 'device': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
|
'data': { 'device': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
|
||||||
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int' } }
|
'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
|
||||||
|
'*bps_max': 'int', '*bps_rd_max': 'int',
|
||||||
|
'*bps_wr_max': 'int', '*iops_max': 'int',
|
||||||
|
'*iops_rd_max': 'int', '*iops_wr_max': 'int',
|
||||||
|
'*iops_size': 'int' } }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @block-stream:
|
# @block-stream:
|
||||||
|
@ -34,9 +34,9 @@ STEXI
|
|||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
DEF("convert", img_convert,
|
DEF("convert", img_convert,
|
||||||
"convert [-c] [-p] [-q] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] [-S sparse_size] filename [filename2 [...]] output_filename")
|
"convert [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-O output_fmt] [-o options] [-s snapshot_name] [-S sparse_size] filename [filename2 [...]] output_filename")
|
||||||
STEXI
|
STEXI
|
||||||
@item convert [-c] [-p] [-q] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
@item convert [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
DEF("info", img_info,
|
DEF("info", img_info,
|
||||||
@ -45,6 +45,12 @@ STEXI
|
|||||||
@item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename}
|
@item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename}
|
||||||
ETEXI
|
ETEXI
|
||||||
|
|
||||||
|
DEF("map", img_map,
|
||||||
|
"map [-f fmt] [--output=ofmt] filename")
|
||||||
|
STEXI
|
||||||
|
@item map [-f @var{fmt}] [--output=@var{ofmt}] @var{filename}
|
||||||
|
ETEXI
|
||||||
|
|
||||||
DEF("snapshot", img_snapshot,
|
DEF("snapshot", img_snapshot,
|
||||||
"snapshot [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename")
|
"snapshot [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename")
|
||||||
STEXI
|
STEXI
|
||||||
|
317
qemu-img.c
317
qemu-img.c
@ -103,6 +103,8 @@ static void help(void)
|
|||||||
" '-S' indicates the consecutive number of bytes that must contain only zeros\n"
|
" '-S' indicates the consecutive number of bytes that must contain only zeros\n"
|
||||||
" for qemu-img to create a sparse image during conversion\n"
|
" for qemu-img to create a sparse image during conversion\n"
|
||||||
" '--output' takes the format in which the output must be done (human or json)\n"
|
" '--output' takes the format in which the output must be done (human or json)\n"
|
||||||
|
" '-n' skips the target volume creation (useful if the volume is created\n"
|
||||||
|
" prior to running qemu-img)\n"
|
||||||
"\n"
|
"\n"
|
||||||
"Parameters to check subcommand:\n"
|
"Parameters to check subcommand:\n"
|
||||||
" '-r' tries to repair any inconsistencies that are found during the check.\n"
|
" '-r' tries to repair any inconsistencies that are found during the check.\n"
|
||||||
@ -298,7 +300,7 @@ static BlockDriverState *bdrv_new_open(const char *filename,
|
|||||||
return bs;
|
return bs;
|
||||||
fail:
|
fail:
|
||||||
if (bs) {
|
if (bs) {
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -652,7 +654,7 @@ static int img_check(int argc, char **argv)
|
|||||||
|
|
||||||
fail:
|
fail:
|
||||||
qapi_free_ImageCheck(check);
|
qapi_free_ImageCheck(check);
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -722,7 +724,7 @@ static int img_commit(int argc, char **argv)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1104,11 +1106,11 @@ static int img_compare(int argc, char **argv)
|
|||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
bdrv_delete(bs2);
|
bdrv_unref(bs2);
|
||||||
qemu_vfree(buf1);
|
qemu_vfree(buf1);
|
||||||
qemu_vfree(buf2);
|
qemu_vfree(buf2);
|
||||||
out2:
|
out2:
|
||||||
bdrv_delete(bs1);
|
bdrv_unref(bs1);
|
||||||
out3:
|
out3:
|
||||||
qemu_progress_end();
|
qemu_progress_end();
|
||||||
return ret;
|
return ret;
|
||||||
@ -1116,7 +1118,8 @@ out3:
|
|||||||
|
|
||||||
static int img_convert(int argc, char **argv)
|
static int img_convert(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size, cluster_sectors;
|
int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size,
|
||||||
|
cluster_sectors, skip_create;
|
||||||
int progress = 0, flags;
|
int progress = 0, flags;
|
||||||
const char *fmt, *out_fmt, *cache, *out_baseimg, *out_filename;
|
const char *fmt, *out_fmt, *cache, *out_baseimg, *out_filename;
|
||||||
BlockDriver *drv, *proto_drv;
|
BlockDriver *drv, *proto_drv;
|
||||||
@ -1139,8 +1142,9 @@ static int img_convert(int argc, char **argv)
|
|||||||
cache = "unsafe";
|
cache = "unsafe";
|
||||||
out_baseimg = NULL;
|
out_baseimg = NULL;
|
||||||
compress = 0;
|
compress = 0;
|
||||||
|
skip_create = 0;
|
||||||
for(;;) {
|
for(;;) {
|
||||||
c = getopt(argc, argv, "f:O:B:s:hce6o:pS:t:q");
|
c = getopt(argc, argv, "f:O:B:s:hce6o:pS:t:qn");
|
||||||
if (c == -1) {
|
if (c == -1) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1197,6 +1201,9 @@ static int img_convert(int argc, char **argv)
|
|||||||
case 'q':
|
case 'q':
|
||||||
quiet = true;
|
quiet = true;
|
||||||
break;
|
break;
|
||||||
|
case 'n':
|
||||||
|
skip_create = 1;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1329,20 +1336,22 @@ static int img_convert(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create the new image */
|
if (!skip_create) {
|
||||||
ret = bdrv_create(drv, out_filename, param);
|
/* Create the new image */
|
||||||
if (ret < 0) {
|
ret = bdrv_create(drv, out_filename, param);
|
||||||
if (ret == -ENOTSUP) {
|
if (ret < 0) {
|
||||||
error_report("Formatting not supported for file format '%s'",
|
if (ret == -ENOTSUP) {
|
||||||
out_fmt);
|
error_report("Formatting not supported for file format '%s'",
|
||||||
} else if (ret == -EFBIG) {
|
out_fmt);
|
||||||
error_report("The image size is too large for file format '%s'",
|
} else if (ret == -EFBIG) {
|
||||||
out_fmt);
|
error_report("The image size is too large for file format '%s'",
|
||||||
} else {
|
out_fmt);
|
||||||
error_report("%s: error while converting %s: %s",
|
} else {
|
||||||
out_filename, out_fmt, strerror(-ret));
|
error_report("%s: error while converting %s: %s",
|
||||||
|
out_filename, out_fmt, strerror(-ret));
|
||||||
|
}
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
flags = BDRV_O_RDWR;
|
flags = BDRV_O_RDWR;
|
||||||
@ -1363,6 +1372,20 @@ static int img_convert(int argc, char **argv)
|
|||||||
bdrv_get_geometry(bs[0], &bs_sectors);
|
bdrv_get_geometry(bs[0], &bs_sectors);
|
||||||
buf = qemu_blockalign(out_bs, IO_BUF_SIZE);
|
buf = qemu_blockalign(out_bs, IO_BUF_SIZE);
|
||||||
|
|
||||||
|
if (skip_create) {
|
||||||
|
int64_t output_length = bdrv_getlength(out_bs);
|
||||||
|
if (output_length < 0) {
|
||||||
|
error_report("unable to get output image length: %s\n",
|
||||||
|
strerror(-output_length));
|
||||||
|
ret = -1;
|
||||||
|
goto out;
|
||||||
|
} else if (output_length < total_sectors << BDRV_SECTOR_BITS) {
|
||||||
|
error_report("output file is smaller than input file");
|
||||||
|
ret = -1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (compress) {
|
if (compress) {
|
||||||
ret = bdrv_get_info(out_bs, &bdi);
|
ret = bdrv_get_info(out_bs, &bdi);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -1479,21 +1502,26 @@ static int img_convert(int argc, char **argv)
|
|||||||
n = bs_offset + bs_sectors - sector_num;
|
n = bs_offset + bs_sectors - sector_num;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_zero_init) {
|
/* If the output image is being created as a copy on write image,
|
||||||
/* If the output image is being created as a copy on write image,
|
assume that sectors which are unallocated in the input image
|
||||||
assume that sectors which are unallocated in the input image
|
are present in both the output's and input's base images (no
|
||||||
are present in both the output's and input's base images (no
|
need to copy them). */
|
||||||
need to copy them). */
|
if (out_baseimg) {
|
||||||
if (out_baseimg) {
|
ret = bdrv_is_allocated(bs[bs_i], sector_num - bs_offset,
|
||||||
if (!bdrv_is_allocated(bs[bs_i], sector_num - bs_offset,
|
n, &n1);
|
||||||
n, &n1)) {
|
if (ret < 0) {
|
||||||
sector_num += n1;
|
error_report("error while reading metadata for sector "
|
||||||
continue;
|
"%" PRId64 ": %s",
|
||||||
}
|
sector_num - bs_offset, strerror(-ret));
|
||||||
/* The next 'n1' sectors are allocated in the input image. Copy
|
goto out;
|
||||||
only those as they may be followed by unallocated sectors. */
|
|
||||||
n = n1;
|
|
||||||
}
|
}
|
||||||
|
if (!ret) {
|
||||||
|
sector_num += n1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* The next 'n1' sectors are allocated in the input image. Copy
|
||||||
|
only those as they may be followed by unallocated sectors. */
|
||||||
|
n = n1;
|
||||||
} else {
|
} else {
|
||||||
n1 = n;
|
n1 = n;
|
||||||
}
|
}
|
||||||
@ -1509,14 +1537,7 @@ static int img_convert(int argc, char **argv)
|
|||||||
should add a specific call to have the info to go faster */
|
should add a specific call to have the info to go faster */
|
||||||
buf1 = buf;
|
buf1 = buf;
|
||||||
while (n > 0) {
|
while (n > 0) {
|
||||||
/* If the output image is being created as a copy on write image,
|
if (!has_zero_init ||
|
||||||
copy all sectors even the ones containing only NUL bytes,
|
|
||||||
because they may differ from the sectors in the base image.
|
|
||||||
|
|
||||||
If the output is to a host device, we also write out
|
|
||||||
sectors that are entirely 0, since whatever data was
|
|
||||||
already there is garbage, not 0s. */
|
|
||||||
if (!has_zero_init || out_baseimg ||
|
|
||||||
is_allocated_sectors_min(buf1, n, &n1, min_sparse)) {
|
is_allocated_sectors_min(buf1, n, &n1, min_sparse)) {
|
||||||
ret = bdrv_write(out_bs, sector_num, buf1, n1);
|
ret = bdrv_write(out_bs, sector_num, buf1, n1);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -1538,12 +1559,12 @@ out:
|
|||||||
free_option_parameters(param);
|
free_option_parameters(param);
|
||||||
qemu_vfree(buf);
|
qemu_vfree(buf);
|
||||||
if (out_bs) {
|
if (out_bs) {
|
||||||
bdrv_delete(out_bs);
|
bdrv_unref(out_bs);
|
||||||
}
|
}
|
||||||
if (bs) {
|
if (bs) {
|
||||||
for (bs_i = 0; bs_i < bs_n; bs_i++) {
|
for (bs_i = 0; bs_i < bs_n; bs_i++) {
|
||||||
if (bs[bs_i]) {
|
if (bs[bs_i]) {
|
||||||
bdrv_delete(bs[bs_i]);
|
bdrv_unref(bs[bs_i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g_free(bs);
|
g_free(bs);
|
||||||
@ -1681,7 +1702,7 @@ static ImageInfoList *collect_image_info_list(const char *filename,
|
|||||||
*last = elem;
|
*last = elem;
|
||||||
last = &elem->next;
|
last = &elem->next;
|
||||||
|
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
|
|
||||||
filename = fmt = NULL;
|
filename = fmt = NULL;
|
||||||
if (chain) {
|
if (chain) {
|
||||||
@ -1780,6 +1801,197 @@ static int img_info(int argc, char **argv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct MapEntry {
|
||||||
|
int flags;
|
||||||
|
int depth;
|
||||||
|
int64_t start;
|
||||||
|
int64_t length;
|
||||||
|
int64_t offset;
|
||||||
|
BlockDriverState *bs;
|
||||||
|
} MapEntry;
|
||||||
|
|
||||||
|
static void dump_map_entry(OutputFormat output_format, MapEntry *e,
|
||||||
|
MapEntry *next)
|
||||||
|
{
|
||||||
|
switch (output_format) {
|
||||||
|
case OFORMAT_HUMAN:
|
||||||
|
if ((e->flags & BDRV_BLOCK_DATA) &&
|
||||||
|
!(e->flags & BDRV_BLOCK_OFFSET_VALID)) {
|
||||||
|
error_report("File contains external, encrypted or compressed clusters.");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if ((e->flags & (BDRV_BLOCK_DATA|BDRV_BLOCK_ZERO)) == BDRV_BLOCK_DATA) {
|
||||||
|
printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n",
|
||||||
|
e->start, e->length, e->offset, e->bs->filename);
|
||||||
|
}
|
||||||
|
/* This format ignores the distinction between 0, ZERO and ZERO|DATA.
|
||||||
|
* Modify the flags here to allow more coalescing.
|
||||||
|
*/
|
||||||
|
if (next &&
|
||||||
|
(next->flags & (BDRV_BLOCK_DATA|BDRV_BLOCK_ZERO)) != BDRV_BLOCK_DATA) {
|
||||||
|
next->flags &= ~BDRV_BLOCK_DATA;
|
||||||
|
next->flags |= BDRV_BLOCK_ZERO;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OFORMAT_JSON:
|
||||||
|
printf("%s{ \"start\": %"PRId64", \"length\": %"PRId64", \"depth\": %d,"
|
||||||
|
" \"zero\": %s, \"data\": %s",
|
||||||
|
(e->start == 0 ? "[" : ",\n"),
|
||||||
|
e->start, e->length, e->depth,
|
||||||
|
(e->flags & BDRV_BLOCK_ZERO) ? "true" : "false",
|
||||||
|
(e->flags & BDRV_BLOCK_DATA) ? "true" : "false");
|
||||||
|
if (e->flags & BDRV_BLOCK_OFFSET_VALID) {
|
||||||
|
printf(", 'offset': %"PRId64"", e->offset);
|
||||||
|
}
|
||||||
|
putchar('}');
|
||||||
|
|
||||||
|
if (!next) {
|
||||||
|
printf("]\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_block_status(BlockDriverState *bs, int64_t sector_num,
|
||||||
|
int nb_sectors, MapEntry *e)
|
||||||
|
{
|
||||||
|
int64_t ret;
|
||||||
|
int depth;
|
||||||
|
|
||||||
|
/* As an optimization, we could cache the current range of unallocated
|
||||||
|
* clusters in each file of the chain, and avoid querying the same
|
||||||
|
* range repeatedly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
depth = 0;
|
||||||
|
for (;;) {
|
||||||
|
ret = bdrv_get_block_status(bs, sector_num, nb_sectors, &nb_sectors);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
assert(nb_sectors);
|
||||||
|
if (ret & (BDRV_BLOCK_ZERO|BDRV_BLOCK_DATA)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bs = bs->backing_hd;
|
||||||
|
if (bs == NULL) {
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
depth++;
|
||||||
|
}
|
||||||
|
|
||||||
|
e->start = sector_num * BDRV_SECTOR_SIZE;
|
||||||
|
e->length = nb_sectors * BDRV_SECTOR_SIZE;
|
||||||
|
e->flags = ret & ~BDRV_BLOCK_OFFSET_MASK;
|
||||||
|
e->offset = ret & BDRV_BLOCK_OFFSET_MASK;
|
||||||
|
e->depth = depth;
|
||||||
|
e->bs = bs;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int img_map(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
OutputFormat output_format = OFORMAT_HUMAN;
|
||||||
|
BlockDriverState *bs;
|
||||||
|
const char *filename, *fmt, *output;
|
||||||
|
int64_t length;
|
||||||
|
MapEntry curr = { .length = 0 }, next;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
fmt = NULL;
|
||||||
|
output = NULL;
|
||||||
|
for (;;) {
|
||||||
|
int option_index = 0;
|
||||||
|
static const struct option long_options[] = {
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
{"format", required_argument, 0, 'f'},
|
||||||
|
{"output", required_argument, 0, OPTION_OUTPUT},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
c = getopt_long(argc, argv, "f:h",
|
||||||
|
long_options, &option_index);
|
||||||
|
if (c == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch (c) {
|
||||||
|
case '?':
|
||||||
|
case 'h':
|
||||||
|
help();
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
fmt = optarg;
|
||||||
|
break;
|
||||||
|
case OPTION_OUTPUT:
|
||||||
|
output = optarg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (optind >= argc) {
|
||||||
|
help();
|
||||||
|
}
|
||||||
|
filename = argv[optind++];
|
||||||
|
|
||||||
|
if (output && !strcmp(output, "json")) {
|
||||||
|
output_format = OFORMAT_JSON;
|
||||||
|
} else if (output && !strcmp(output, "human")) {
|
||||||
|
output_format = OFORMAT_HUMAN;
|
||||||
|
} else if (output) {
|
||||||
|
error_report("--output must be used with human or json as argument.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS, true, false);
|
||||||
|
if (!bs) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output_format == OFORMAT_HUMAN) {
|
||||||
|
printf("%-16s%-16s%-16s%s\n", "Offset", "Length", "Mapped to", "File");
|
||||||
|
}
|
||||||
|
|
||||||
|
length = bdrv_getlength(bs);
|
||||||
|
while (curr.start + curr.length < length) {
|
||||||
|
int64_t nsectors_left;
|
||||||
|
int64_t sector_num;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
sector_num = (curr.start + curr.length) >> BDRV_SECTOR_BITS;
|
||||||
|
|
||||||
|
/* Probe up to 1 GiB at a time. */
|
||||||
|
nsectors_left = DIV_ROUND_UP(length, BDRV_SECTOR_SIZE) - sector_num;
|
||||||
|
n = MIN(1 << (30 - BDRV_SECTOR_BITS), nsectors_left);
|
||||||
|
ret = get_block_status(bs, sector_num, n, &next);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
error_report("Could not read file metadata: %s", strerror(-ret));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curr.length != 0 && curr.flags == next.flags &&
|
||||||
|
curr.depth == next.depth &&
|
||||||
|
((curr.flags & BDRV_BLOCK_OFFSET_VALID) == 0 ||
|
||||||
|
curr.offset + curr.length == next.offset)) {
|
||||||
|
curr.length += next.length;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curr.length > 0) {
|
||||||
|
dump_map_entry(output_format, &curr, &next);
|
||||||
|
}
|
||||||
|
curr = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
dump_map_entry(output_format, &curr, NULL);
|
||||||
|
|
||||||
|
out:
|
||||||
|
bdrv_unref(bs);
|
||||||
|
return ret < 0;
|
||||||
|
}
|
||||||
|
|
||||||
#define SNAPSHOT_LIST 1
|
#define SNAPSHOT_LIST 1
|
||||||
#define SNAPSHOT_CREATE 2
|
#define SNAPSHOT_CREATE 2
|
||||||
#define SNAPSHOT_APPLY 3
|
#define SNAPSHOT_APPLY 3
|
||||||
@ -1895,7 +2107,7 @@ static int img_snapshot(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -2076,6 +2288,11 @@ static int img_rebase(int argc, char **argv)
|
|||||||
|
|
||||||
/* If the cluster is allocated, we don't need to take action */
|
/* If the cluster is allocated, we don't need to take action */
|
||||||
ret = bdrv_is_allocated(bs, sector, n, &n);
|
ret = bdrv_is_allocated(bs, sector, n, &n);
|
||||||
|
if (ret < 0) {
|
||||||
|
error_report("error while reading image metadata: %s",
|
||||||
|
strerror(-ret));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
if (ret) {
|
if (ret) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2170,14 +2387,14 @@ out:
|
|||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
if (!unsafe) {
|
if (!unsafe) {
|
||||||
if (bs_old_backing != NULL) {
|
if (bs_old_backing != NULL) {
|
||||||
bdrv_delete(bs_old_backing);
|
bdrv_unref(bs_old_backing);
|
||||||
}
|
}
|
||||||
if (bs_new_backing != NULL) {
|
if (bs_new_backing != NULL) {
|
||||||
bdrv_delete(bs_new_backing);
|
bdrv_unref(bs_new_backing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -2300,7 +2517,7 @@ static int img_resize(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
if (bs) {
|
if (bs) {
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
}
|
}
|
||||||
if (ret) {
|
if (ret) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -96,6 +96,14 @@ Second image format
|
|||||||
Strict mode - fail on on different image size or sector allocation
|
Strict mode - fail on on different image size or sector allocation
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
Parameters to convert subcommand:
|
||||||
|
|
||||||
|
@table @option
|
||||||
|
|
||||||
|
@item -n
|
||||||
|
Skip the creation of the target volume
|
||||||
|
@end table
|
||||||
|
|
||||||
Command description:
|
Command description:
|
||||||
|
|
||||||
@table @option
|
@table @option
|
||||||
@ -171,7 +179,7 @@ Error on reading data
|
|||||||
|
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
@item convert [-c] [-p] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
@item convert [-c] [-p] [-n] [-f @var{fmt}] [-t @var{cache}] [-O @var{output_fmt}] [-o @var{options}] [-s @var{snapshot_name}] [-S @var{sparse_size}] @var{filename} [@var{filename2} [...]] @var{output_filename}
|
||||||
|
|
||||||
Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
|
Convert the disk image @var{filename} or a snapshot @var{snapshot_name} to disk image @var{output_filename}
|
||||||
using format @var{output_fmt}. It can be optionally compressed (@code{-c}
|
using format @var{output_fmt}. It can be optionally compressed (@code{-c}
|
||||||
@ -190,6 +198,11 @@ created as a copy on write image of the specified base image; the
|
|||||||
@var{backing_file} should have the same content as the input's base image,
|
@var{backing_file} should have the same content as the input's base image,
|
||||||
however the path, image format, etc may differ.
|
however the path, image format, etc may differ.
|
||||||
|
|
||||||
|
If the @code{-n} option is specified, the target volume creation will be
|
||||||
|
skipped. This is useful for formats such as @code{rbd} if the target
|
||||||
|
volume has already been created with site specific options that cannot
|
||||||
|
be supplied through qemu-img.
|
||||||
|
|
||||||
@item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename}
|
@item info [-f @var{fmt}] [--output=@var{ofmt}] [--backing-chain] @var{filename}
|
||||||
|
|
||||||
Give information about the disk image @var{filename}. Use it in
|
Give information about the disk image @var{filename}. Use it in
|
||||||
@ -213,6 +226,61 @@ To enumerate information about each disk image in the above chain, starting from
|
|||||||
qemu-img info --backing-chain snap2.qcow2
|
qemu-img info --backing-chain snap2.qcow2
|
||||||
@end example
|
@end example
|
||||||
|
|
||||||
|
@item map [-f @var{fmt}] [--output=@var{ofmt}] @var{filename}
|
||||||
|
|
||||||
|
Dump the metadata of image @var{filename} and its backing file chain.
|
||||||
|
In particular, this commands dumps the allocation state of every sector
|
||||||
|
of @var{filename}, together with the topmost file that allocates it in
|
||||||
|
the backing file chain.
|
||||||
|
|
||||||
|
Two option formats are possible. The default format (@code{human})
|
||||||
|
only dumps known-nonzero areas of the file. Known-zero parts of the
|
||||||
|
file are omitted altogether, and likewise for parts that are not allocated
|
||||||
|
throughout the chain. @command{qemu-img} output will identify a file
|
||||||
|
from where the data can be read, and the offset in the file. Each line
|
||||||
|
will include four fields, the first three of which are hexadecimal
|
||||||
|
numbers. For example the first line of:
|
||||||
|
@example
|
||||||
|
Offset Length Mapped to File
|
||||||
|
0 0x20000 0x50000 /tmp/overlay.qcow2
|
||||||
|
0x100000 0x10000 0x95380000 /tmp/backing.qcow2
|
||||||
|
@end example
|
||||||
|
@noindent
|
||||||
|
means that 0x20000 (131072) bytes starting at offset 0 in the image are
|
||||||
|
available in /tmp/overlay.qcow2 (opened in @code{raw} format) starting
|
||||||
|
at offset 0x50000 (327680). Data that is compressed, encrypted, or
|
||||||
|
otherwise not available in raw format will cause an error if @code{human}
|
||||||
|
format is in use. Note that file names can include newlines, thus it is
|
||||||
|
not safe to parse this output format in scripts.
|
||||||
|
|
||||||
|
The alternative format @code{json} will return an array of dictionaries
|
||||||
|
in JSON format. It will include similar information in
|
||||||
|
the @code{start}, @code{length}, @code{offset} fields;
|
||||||
|
it will also include other more specific information:
|
||||||
|
@itemize @minus
|
||||||
|
@item
|
||||||
|
whether the sectors contain actual data or not (boolean field @code{data};
|
||||||
|
if false, the sectors are either unallocated or stored as optimized
|
||||||
|
all-zero clusters);
|
||||||
|
|
||||||
|
@item
|
||||||
|
whether the data is known to read as zero (boolean field @code{zero});
|
||||||
|
|
||||||
|
@item
|
||||||
|
in order to make the output shorter, the target file is expressed as
|
||||||
|
a @code{depth}; for example, a depth of 2 refers to the backing file
|
||||||
|
of the backing file of @var{filename}.
|
||||||
|
@end itemize
|
||||||
|
|
||||||
|
In JSON format, the @code{offset} field is optional; it is absent in
|
||||||
|
cases where @code{human} format would omit the entry or exit with an error.
|
||||||
|
If @code{data} is false and the @code{offset} field is present, the
|
||||||
|
corresponding sectors in the file are not yet in use, but they are
|
||||||
|
preallocated.
|
||||||
|
|
||||||
|
For more information, consult @file{include/block/block.h} in QEMU's
|
||||||
|
source code.
|
||||||
|
|
||||||
@item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot} ] @var{filename}
|
@item snapshot [-l | -a @var{snapshot} | -c @var{snapshot} | -d @var{snapshot} ] @var{filename}
|
||||||
|
|
||||||
List, apply, create or delete snapshots in image @var{filename}.
|
List, apply, create or delete snapshots in image @var{filename}.
|
||||||
|
@ -1830,6 +1830,10 @@ static int alloc_f(BlockDriverState *bs, int argc, char **argv)
|
|||||||
sector_num = offset >> 9;
|
sector_num = offset >> 9;
|
||||||
while (remaining) {
|
while (remaining) {
|
||||||
ret = bdrv_is_allocated(bs, sector_num, remaining, &num);
|
ret = bdrv_is_allocated(bs, sector_num, remaining, &num);
|
||||||
|
if (ret < 0) {
|
||||||
|
printf("is_allocated failed: %s\n", strerror(-ret));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
sector_num += num;
|
sector_num += num;
|
||||||
remaining -= num;
|
remaining -= num;
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -32,7 +32,7 @@ static char **cmdline;
|
|||||||
|
|
||||||
static int close_f(BlockDriverState *bs, int argc, char **argv)
|
static int close_f(BlockDriverState *bs, int argc, char **argv)
|
||||||
{
|
{
|
||||||
bdrv_delete(bs);
|
bdrv_unref(bs);
|
||||||
qemuio_bs = NULL;
|
qemuio_bs = NULL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ static int openfile(char *name, int flags, int growable)
|
|||||||
|
|
||||||
if (bdrv_open(qemuio_bs, name, NULL, flags, NULL) < 0) {
|
if (bdrv_open(qemuio_bs, name, NULL, flags, NULL) < 0) {
|
||||||
fprintf(stderr, "%s: can't open device %s\n", progname, name);
|
fprintf(stderr, "%s: can't open device %s\n", progname, name);
|
||||||
bdrv_delete(qemuio_bs);
|
bdrv_unref(qemuio_bs);
|
||||||
qemuio_bs = NULL;
|
qemuio_bs = NULL;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -422,7 +422,7 @@ int main(int argc, char **argv)
|
|||||||
bdrv_drain_all();
|
bdrv_drain_all();
|
||||||
|
|
||||||
if (qemuio_bs) {
|
if (qemuio_bs) {
|
||||||
bdrv_delete(qemuio_bs);
|
bdrv_unref(qemuio_bs);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -409,7 +409,11 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive,
|
|||||||
" [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n"
|
" [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n"
|
||||||
" [,serial=s][,addr=A][,id=name][,aio=threads|native]\n"
|
" [,serial=s][,addr=A][,id=name][,aio=threads|native]\n"
|
||||||
" [,readonly=on|off][,copy-on-read=on|off]\n"
|
" [,readonly=on|off][,copy-on-read=on|off]\n"
|
||||||
" [[,bps=b]|[[,bps_rd=r][,bps_wr=w]]][[,iops=i]|[[,iops_rd=r][,iops_wr=w]]\n"
|
" [[,bps=b]|[[,bps_rd=r][,bps_wr=w]]]\n"
|
||||||
|
" [[,iops=i]|[[,iops_rd=r][,iops_wr=w]]]\n"
|
||||||
|
" [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n"
|
||||||
|
" [[,iops_max=im]|[[,iops_rd_max=irm][,iops_wr_max=iwm]]]\n"
|
||||||
|
" [[,iops_size=is]]\n"
|
||||||
" use 'file' as a drive image\n", QEMU_ARCH_ALL)
|
" use 'file' as a drive image\n", QEMU_ARCH_ALL)
|
||||||
STEXI
|
STEXI
|
||||||
@item -drive @var{option}[,@var{option}[,@var{option}[,...]]]
|
@item -drive @var{option}[,@var{option}[,@var{option}[,...]]]
|
||||||
|
@ -1389,7 +1389,7 @@ EQMP
|
|||||||
|
|
||||||
{
|
{
|
||||||
.name = "block_set_io_throttle",
|
.name = "block_set_io_throttle",
|
||||||
.args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l",
|
.args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?",
|
||||||
.mhandler.cmd_new = qmp_marshal_input_block_set_io_throttle,
|
.mhandler.cmd_new = qmp_marshal_input_block_set_io_throttle,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1408,6 +1408,13 @@ Arguments:
|
|||||||
- "iops": total I/O operations per second (json-int)
|
- "iops": total I/O operations per second (json-int)
|
||||||
- "iops_rd": read I/O operations per second (json-int)
|
- "iops_rd": read I/O operations per second (json-int)
|
||||||
- "iops_wr": write I/O operations per second (json-int)
|
- "iops_wr": write I/O operations per second (json-int)
|
||||||
|
- "bps_max": total max in bytes (json-int)
|
||||||
|
- "bps_rd_max": read max in bytes (json-int)
|
||||||
|
- "bps_wr_max": write max in bytes (json-int)
|
||||||
|
- "iops_max": total I/O operations max (json-int)
|
||||||
|
- "iops_rd_max": read I/O operations max (json-int)
|
||||||
|
- "iops_wr_max": write I/O operations max (json-int)
|
||||||
|
- "iops_size": I/O size in bytes when limiting (json-int)
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@ -1417,7 +1424,14 @@ Example:
|
|||||||
"bps_wr": 0,
|
"bps_wr": 0,
|
||||||
"iops": 0,
|
"iops": 0,
|
||||||
"iops_rd": 0,
|
"iops_rd": 0,
|
||||||
"iops_wr": 0 } }
|
"iops_wr": 0,
|
||||||
|
"bps_max": 8000000,
|
||||||
|
"bps_rd_max": 0,
|
||||||
|
"bps_wr_max": 0,
|
||||||
|
"iops_max": 0,
|
||||||
|
"iops_rd_max": 0,
|
||||||
|
"iops_wr_max": 0,
|
||||||
|
"iops_size": 0 } }
|
||||||
<- { "return": {} }
|
<- { "return": {} }
|
||||||
|
|
||||||
EQMP
|
EQMP
|
||||||
@ -1758,6 +1772,13 @@ Each json-object contain the following:
|
|||||||
- "iops": limit total I/O operations per second (json-int)
|
- "iops": limit total I/O operations per second (json-int)
|
||||||
- "iops_rd": limit read operations per second (json-int)
|
- "iops_rd": limit read operations per second (json-int)
|
||||||
- "iops_wr": limit write operations per second (json-int)
|
- "iops_wr": limit write operations per second (json-int)
|
||||||
|
- "bps_max": total max in bytes (json-int)
|
||||||
|
- "bps_rd_max": read max in bytes (json-int)
|
||||||
|
- "bps_wr_max": write max in bytes (json-int)
|
||||||
|
- "iops_max": total I/O operations max (json-int)
|
||||||
|
- "iops_rd_max": read I/O operations max (json-int)
|
||||||
|
- "iops_wr_max": write I/O operations max (json-int)
|
||||||
|
- "iops_size": I/O size when limiting by iops (json-int)
|
||||||
- "image": the detail of the image, it is a json-object containing
|
- "image": the detail of the image, it is a json-object containing
|
||||||
the following:
|
the following:
|
||||||
- "filename": image file name (json-string)
|
- "filename": image file name (json-string)
|
||||||
@ -1827,6 +1848,13 @@ Example:
|
|||||||
"iops":1000000,
|
"iops":1000000,
|
||||||
"iops_rd":0,
|
"iops_rd":0,
|
||||||
"iops_wr":0,
|
"iops_wr":0,
|
||||||
|
"bps_max": 8000000,
|
||||||
|
"bps_rd_max": 0,
|
||||||
|
"bps_wr_max": 0,
|
||||||
|
"iops_max": 0,
|
||||||
|
"iops_rd_max": 0,
|
||||||
|
"iops_wr_max": 0,
|
||||||
|
"iops_size": 0,
|
||||||
"image":{
|
"image":{
|
||||||
"filename":"disks/test.qcow2",
|
"filename":"disks/test.qcow2",
|
||||||
"format":"qcow2",
|
"format":"qcow2",
|
||||||
|
@ -31,6 +31,7 @@ check-unit-y += tests/test-visitor-serialization$(EXESUF)
|
|||||||
check-unit-y += tests/test-iov$(EXESUF)
|
check-unit-y += tests/test-iov$(EXESUF)
|
||||||
gcov-files-test-iov-y = util/iov.c
|
gcov-files-test-iov-y = util/iov.c
|
||||||
check-unit-y += tests/test-aio$(EXESUF)
|
check-unit-y += tests/test-aio$(EXESUF)
|
||||||
|
check-unit-y += tests/test-throttle$(EXESUF)
|
||||||
gcov-files-test-aio-$(CONFIG_WIN32) = aio-win32.c
|
gcov-files-test-aio-$(CONFIG_WIN32) = aio-win32.c
|
||||||
gcov-files-test-aio-$(CONFIG_POSIX) = aio-posix.c
|
gcov-files-test-aio-$(CONFIG_POSIX) = aio-posix.c
|
||||||
check-unit-y += tests/test-thread-pool$(EXESUF)
|
check-unit-y += tests/test-thread-pool$(EXESUF)
|
||||||
@ -120,6 +121,7 @@ tests/check-qfloat$(EXESUF): tests/check-qfloat.o libqemuutil.a
|
|||||||
tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a
|
tests/check-qjson$(EXESUF): tests/check-qjson.o libqemuutil.a libqemustub.a
|
||||||
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil.a libqemustub.a
|
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(block-obj-y) libqemuutil.a libqemustub.a
|
||||||
tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a
|
tests/test-aio$(EXESUF): tests/test-aio.o $(block-obj-y) libqemuutil.a libqemustub.a
|
||||||
|
tests/test-throttle$(EXESUF): tests/test-throttle.o $(block-obj-y) libqemuutil.a libqemustub.a
|
||||||
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemuutil.a libqemustub.a
|
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(block-obj-y) libqemuutil.a libqemustub.a
|
||||||
tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a
|
tests/test-iov$(EXESUF): tests/test-iov.o libqemuutil.a
|
||||||
tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a
|
tests/test-hbitmap$(EXESUF): tests/test-hbitmap.o libqemuutil.a libqemustub.a
|
||||||
|
@ -126,62 +126,64 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
|||||||
Event: l2_update; errno: 5; imm: off; once: on; write
|
Event: l2_update; errno: 5; imm: off; once: on; write
|
||||||
write failed: Input/output error
|
write failed: Input/output error
|
||||||
|
|
||||||
128 leaked clusters were found on the image.
|
127 leaked clusters were found on the image.
|
||||||
This means waste of disk space, but no harm to data.
|
This means waste of disk space, but no harm to data.
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l2_update; errno: 5; imm: off; once: on; write -b
|
Event: l2_update; errno: 5; imm: off; once: on; write -b
|
||||||
write failed: Input/output error
|
write failed: Input/output error
|
||||||
|
|
||||||
128 leaked clusters were found on the image.
|
127 leaked clusters were found on the image.
|
||||||
This means waste of disk space, but no harm to data.
|
This means waste of disk space, but no harm to data.
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l2_update; errno: 5; imm: off; once: off; write
|
Event: l2_update; errno: 5; imm: off; once: off; write
|
||||||
write failed: Input/output error
|
write failed: Input/output error
|
||||||
|
|
||||||
128 leaked clusters were found on the image.
|
127 leaked clusters were found on the image.
|
||||||
This means waste of disk space, but no harm to data.
|
This means waste of disk space, but no harm to data.
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l2_update; errno: 5; imm: off; once: off; write -b
|
Event: l2_update; errno: 5; imm: off; once: off; write -b
|
||||||
write failed: Input/output error
|
write failed: Input/output error
|
||||||
|
|
||||||
128 leaked clusters were found on the image.
|
127 leaked clusters were found on the image.
|
||||||
This means waste of disk space, but no harm to data.
|
This means waste of disk space, but no harm to data.
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l2_update; errno: 28; imm: off; once: on; write
|
Event: l2_update; errno: 28; imm: off; once: on; write
|
||||||
write failed: No space left on device
|
write failed: No space left on device
|
||||||
|
|
||||||
128 leaked clusters were found on the image.
|
127 leaked clusters were found on the image.
|
||||||
This means waste of disk space, but no harm to data.
|
This means waste of disk space, but no harm to data.
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l2_update; errno: 28; imm: off; once: on; write -b
|
Event: l2_update; errno: 28; imm: off; once: on; write -b
|
||||||
write failed: No space left on device
|
write failed: No space left on device
|
||||||
|
|
||||||
128 leaked clusters were found on the image.
|
127 leaked clusters were found on the image.
|
||||||
This means waste of disk space, but no harm to data.
|
This means waste of disk space, but no harm to data.
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l2_update; errno: 28; imm: off; once: off; write
|
Event: l2_update; errno: 28; imm: off; once: off; write
|
||||||
write failed: No space left on device
|
write failed: No space left on device
|
||||||
|
|
||||||
128 leaked clusters were found on the image.
|
127 leaked clusters were found on the image.
|
||||||
This means waste of disk space, but no harm to data.
|
This means waste of disk space, but no harm to data.
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l2_update; errno: 28; imm: off; once: off; write -b
|
Event: l2_update; errno: 28; imm: off; once: off; write -b
|
||||||
write failed: No space left on device
|
write failed: No space left on device
|
||||||
|
|
||||||
128 leaked clusters were found on the image.
|
127 leaked clusters were found on the image.
|
||||||
This means waste of disk space, but no harm to data.
|
This means waste of disk space, but no harm to data.
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l2_alloc.write; errno: 5; imm: off; once: on; write
|
Event: l2_alloc.write; errno: 5; imm: off; once: on; write
|
||||||
write failed: Input/output error
|
write failed: Input/output error
|
||||||
No errors were found on the image.
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l2_alloc.write; errno: 5; imm: off; once: on; write -b
|
Event: l2_alloc.write; errno: 5; imm: off; once: on; write -b
|
||||||
@ -205,7 +207,9 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
|||||||
|
|
||||||
Event: l2_alloc.write; errno: 28; imm: off; once: on; write
|
Event: l2_alloc.write; errno: 28; imm: off; once: on; write
|
||||||
write failed: No space left on device
|
write failed: No space left on device
|
||||||
No errors were found on the image.
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l2_alloc.write; errno: 28; imm: off; once: on; write -b
|
Event: l2_alloc.write; errno: 28; imm: off; once: on; write -b
|
||||||
@ -575,7 +579,6 @@ No errors were found on the image.
|
|||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l1_grow.write_table; errno: 5; imm: off; once: off
|
Event: l1_grow.write_table; errno: 5; imm: off; once: off
|
||||||
qcow2_free_clusters failed: Input/output error
|
|
||||||
write failed: Input/output error
|
write failed: Input/output error
|
||||||
No errors were found on the image.
|
No errors were found on the image.
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
@ -586,7 +589,6 @@ No errors were found on the image.
|
|||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l1_grow.write_table; errno: 28; imm: off; once: off
|
Event: l1_grow.write_table; errno: 28; imm: off; once: off
|
||||||
qcow2_free_clusters failed: No space left on device
|
|
||||||
write failed: No space left on device
|
write failed: No space left on device
|
||||||
No errors were found on the image.
|
No errors were found on the image.
|
||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
@ -597,7 +599,6 @@ No errors were found on the image.
|
|||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l1_grow.activate_table; errno: 5; imm: off; once: off
|
Event: l1_grow.activate_table; errno: 5; imm: off; once: off
|
||||||
qcow2_free_clusters failed: Input/output error
|
|
||||||
write failed: Input/output error
|
write failed: Input/output error
|
||||||
|
|
||||||
96 leaked clusters were found on the image.
|
96 leaked clusters were found on the image.
|
||||||
@ -610,7 +611,6 @@ No errors were found on the image.
|
|||||||
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
Event: l1_grow.activate_table; errno: 28; imm: off; once: off
|
Event: l1_grow.activate_table; errno: 28; imm: off; once: off
|
||||||
qcow2_free_clusters failed: No space left on device
|
|
||||||
write failed: No space left on device
|
write failed: No space left on device
|
||||||
|
|
||||||
96 leaked clusters were found on the image.
|
96 leaked clusters were found on the image.
|
||||||
|
626
tests/qemu-iotests/026.out.nocache
Normal file
626
tests/qemu-iotests/026.out.nocache
Normal file
@ -0,0 +1,626 @@
|
|||||||
|
QA output created by 026
|
||||||
|
Errors while writing 128 kB
|
||||||
|
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_update; errno: 5; imm: off; once: on; write
|
||||||
|
write failed: Input/output error
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_update; errno: 5; imm: off; once: on; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_update; errno: 5; imm: off; once: off; write
|
||||||
|
write failed: Input/output error
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_update; errno: 5; imm: off; once: off; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_update; errno: 28; imm: off; once: on; write
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_update; errno: 28; imm: off; once: on; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_update; errno: 28; imm: off; once: off; write
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_update; errno: 28; imm: off; once: off; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_load; errno: 5; imm: off; once: on; write
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
write failed: Input/output error
|
||||||
|
read failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_load; errno: 5; imm: off; once: on; write -b
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
write failed: Input/output error
|
||||||
|
read failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_load; errno: 5; imm: off; once: off; write
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
write failed: Input/output error
|
||||||
|
read failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_load; errno: 5; imm: off; once: off; write -b
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
write failed: Input/output error
|
||||||
|
read failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_load; errno: 28; imm: off; once: on; write
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
write failed: No space left on device
|
||||||
|
read failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_load; errno: 28; imm: off; once: on; write -b
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
write failed: No space left on device
|
||||||
|
read failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_load; errno: 28; imm: off; once: off; write
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
write failed: No space left on device
|
||||||
|
read failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_load; errno: 28; imm: off; once: off; write -b
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
write failed: No space left on device
|
||||||
|
read failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_update; errno: 5; imm: off; once: on; write
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
127 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_update; errno: 5; imm: off; once: on; write -b
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
127 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_update; errno: 5; imm: off; once: off; write
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
127 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_update; errno: 5; imm: off; once: off; write -b
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
127 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_update; errno: 28; imm: off; once: on; write
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
127 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_update; errno: 28; imm: off; once: on; write -b
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
127 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_update; errno: 28; imm: off; once: off; write
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
127 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_update; errno: 28; imm: off; once: off; write -b
|
||||||
|
wrote 131072/131072 bytes at offset 0
|
||||||
|
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
|
||||||
|
127 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_alloc.write; errno: 5; imm: off; once: on; write
|
||||||
|
write failed: Input/output error
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_alloc.write; errno: 5; imm: off; once: on; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_alloc.write; errno: 5; imm: off; once: off; write
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_alloc.write; errno: 5; imm: off; once: off; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_alloc.write; errno: 28; imm: off; once: on; write
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_alloc.write; errno: 28; imm: off; once: on; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_alloc.write; errno: 28; imm: off; once: off; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l2_alloc.write; errno: 28; imm: off; once: off; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
1 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: write_aio; errno: 5; imm: off; once: on; write
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: write_aio; errno: 5; imm: off; once: on; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: write_aio; errno: 5; imm: off; once: off; write
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: write_aio; errno: 5; imm: off; once: off; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: write_aio; errno: 28; imm: off; once: on; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: write_aio; errno: 28; imm: off; once: on; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: write_aio; errno: 28; imm: off; once: off; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: write_aio; errno: 28; imm: off; once: off; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_load; errno: 5; imm: off; once: on; write
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_load; errno: 5; imm: off; once: on; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_load; errno: 5; imm: off; once: off; write
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_load; errno: 5; imm: off; once: off; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_load; errno: 28; imm: off; once: on; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_load; errno: 28; imm: off; once: on; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_load; errno: 28; imm: off; once: off; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_load; errno: 28; imm: off; once: off; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_update_part; errno: 5; imm: off; once: on; write
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_update_part; errno: 5; imm: off; once: on; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_update_part; errno: 5; imm: off; once: off; write
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_update_part; errno: 5; imm: off; once: off; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_update_part; errno: 28; imm: off; once: on; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_update_part; errno: 28; imm: off; once: on; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_update_part; errno: 28; imm: off; once: off; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_update_part; errno: 28; imm: off; once: off; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc; errno: 5; imm: off; once: on; write
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc; errno: 5; imm: off; once: on; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc; errno: 5; imm: off; once: off; write
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc; errno: 5; imm: off; once: off; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc; errno: 28; imm: off; once: on; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc; errno: 28; imm: off; once: on; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc; errno: 28; imm: off; once: off; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc; errno: 28; imm: off; once: off; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: cluster_alloc; errno: 5; imm: off; once: on; write
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: cluster_alloc; errno: 5; imm: off; once: on; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: cluster_alloc; errno: 5; imm: off; once: off; write
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: cluster_alloc; errno: 5; imm: off; once: off; write -b
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: cluster_alloc; errno: 28; imm: off; once: on; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: cluster_alloc; errno: 28; imm: off; once: on; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: cluster_alloc; errno: 28; imm: off; once: off; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: cluster_alloc; errno: 28; imm: off; once: off; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
|
||||||
|
=== Refcout table growth tests ===
|
||||||
|
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.hookup; errno: 28; imm: off; once: on; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
55 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.hookup; errno: 28; imm: off; once: off; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
251 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.write; errno: 28; imm: off; once: on; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.write; errno: 28; imm: off; once: on; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.write; errno: 28; imm: off; once: off; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.write; errno: 28; imm: off; once: off; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: on; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
10 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
23 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.write_table; errno: 28; imm: off; once: on; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
10 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
23 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.switch_table; errno: 28; imm: off; once: on; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
10 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write -b
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
23 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
|
||||||
|
=== L1 growth tests ===
|
||||||
|
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_grow.alloc_table; errno: 5; imm: off; once: on
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_grow.alloc_table; errno: 5; imm: off; once: off
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_grow.alloc_table; errno: 28; imm: off; once: on
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_grow.alloc_table; errno: 28; imm: off; once: off
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_grow.write_table; errno: 5; imm: off; once: on
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_grow.write_table; errno: 5; imm: off; once: off
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_grow.write_table; errno: 28; imm: off; once: on
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_grow.write_table; errno: 28; imm: off; once: off
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_grow.activate_table; errno: 5; imm: off; once: on
|
||||||
|
write failed: Input/output error
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_grow.activate_table; errno: 5; imm: off; once: off
|
||||||
|
write failed: Input/output error
|
||||||
|
|
||||||
|
96 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_grow.activate_table; errno: 28; imm: off; once: on
|
||||||
|
write failed: No space left on device
|
||||||
|
No errors were found on the image.
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
|
||||||
|
|
||||||
|
Event: l1_grow.activate_table; errno: 28; imm: off; once: off
|
||||||
|
write failed: No space left on device
|
||||||
|
|
||||||
|
96 leaked clusters were found on the image.
|
||||||
|
This means waste of disk space, but no harm to data.
|
||||||
|
*** done
|
@ -12,8 +12,8 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
|||||||
wrote 512/512 bytes at offset 0
|
wrote 512/512 bytes at offset 0
|
||||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
incompatible_features 0x1
|
incompatible_features 0x1
|
||||||
ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0
|
|
||||||
ERROR cluster 5 refcount=0 reference=1
|
ERROR cluster 5 refcount=0 reference=1
|
||||||
|
ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0
|
||||||
|
|
||||||
2 errors were found on the image.
|
2 errors were found on the image.
|
||||||
Data may be corrupted, or further writes to the image may corrupt it.
|
Data may be corrupted, or further writes to the image may corrupt it.
|
||||||
@ -24,7 +24,6 @@ read 512/512 bytes at offset 0
|
|||||||
incompatible_features 0x1
|
incompatible_features 0x1
|
||||||
|
|
||||||
== Repairing the image file must succeed ==
|
== Repairing the image file must succeed ==
|
||||||
ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0
|
|
||||||
Repairing cluster 5 refcount=0 reference=1
|
Repairing cluster 5 refcount=0 reference=1
|
||||||
The following inconsistencies were found and repaired:
|
The following inconsistencies were found and repaired:
|
||||||
|
|
||||||
@ -44,7 +43,6 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
|
|||||||
wrote 512/512 bytes at offset 0
|
wrote 512/512 bytes at offset 0
|
||||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
incompatible_features 0x1
|
incompatible_features 0x1
|
||||||
ERROR OFLAG_COPIED: offset=8000000000050000 refcount=0
|
|
||||||
Repairing cluster 5 refcount=0 reference=1
|
Repairing cluster 5 refcount=0 reference=1
|
||||||
wrote 512/512 bytes at offset 0
|
wrote 512/512 bytes at offset 0
|
||||||
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
97
tests/qemu-iotests/063
Executable file
97
tests/qemu-iotests/063
Executable file
@ -0,0 +1,97 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# test of qemu-img convert -n - convert without creation
|
||||||
|
#
|
||||||
|
# Copyright (C) 2009 Red Hat, Inc.
|
||||||
|
# Copyright (C) 2013 Alex Bligh (alex@alex.org.uk)
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
# creator
|
||||||
|
owner=alex@alex.org.uk
|
||||||
|
|
||||||
|
seq=`basename $0`
|
||||||
|
echo "QA output created by $seq"
|
||||||
|
|
||||||
|
here=`pwd`
|
||||||
|
tmp=/tmp/$$
|
||||||
|
status=1 # failure is the default!
|
||||||
|
|
||||||
|
_cleanup()
|
||||||
|
{
|
||||||
|
_cleanup_test_img
|
||||||
|
rm -f $TEST_IMG.orig $TEST_IMG.raw $TEST_IMG.raw2
|
||||||
|
}
|
||||||
|
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
|
|
||||||
|
# get standard environment, filters and checks
|
||||||
|
. ./common.rc
|
||||||
|
. ./common.filter
|
||||||
|
. ./common.pattern
|
||||||
|
|
||||||
|
_supported_fmt qcow qcow2 vmdk qed raw
|
||||||
|
_supported_proto generic
|
||||||
|
_supported_os Linux
|
||||||
|
|
||||||
|
_make_test_img 4M
|
||||||
|
|
||||||
|
echo "== Testing conversion with -n fails with no target file =="
|
||||||
|
# check .orig file does not exist
|
||||||
|
rm -f $TEST_IMG.orig
|
||||||
|
if $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n $TEST_IMG $TEST_IMG.orig >/dev/null 2>&1; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "== Testing conversion with -n succeeds with a target file =="
|
||||||
|
rm -f $TEST_IMG.orig
|
||||||
|
cp $TEST_IMG $TEST_IMG.orig
|
||||||
|
if ! $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n $TEST_IMG $TEST_IMG.orig ; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "== Testing conversion to raw is the same after conversion with -n =="
|
||||||
|
# compare the raw files
|
||||||
|
if ! $QEMU_IMG convert -f $IMGFMT -O raw $TEST_IMG $TEST_IMG.raw1 ; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! $QEMU_IMG convert -f $IMGFMT -O raw $TEST_IMG.orig $TEST_IMG.raw2 ; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! cmp $TEST_IMG.raw1 $TEST_IMG.raw2 ; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "== Testing conversion back to original format =="
|
||||||
|
if ! $QEMU_IMG convert -f raw -O $IMGFMT -n $TEST_IMG.raw2 $TEST_IMG ; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
_check_test_img
|
||||||
|
|
||||||
|
echo "== Testing conversion to a smaller file fails =="
|
||||||
|
rm -f $TEST_IMG.orig
|
||||||
|
mv $TEST_IMG $TEST_IMG.orig
|
||||||
|
_make_test_img 2M
|
||||||
|
if $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n $TEST_IMG.orig $TEST_IMG >/dev/null 2>&1; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f $TEST_IMG.orig $TEST_IMG.raw $TEST_IMG.raw2
|
||||||
|
|
||||||
|
echo "*** done"
|
||||||
|
rm -f $seq.full
|
||||||
|
status=0
|
||||||
|
exit 0
|
10
tests/qemu-iotests/063.out
Normal file
10
tests/qemu-iotests/063.out
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
QA output created by 063
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304
|
||||||
|
== Testing conversion with -n fails with no target file ==
|
||||||
|
== Testing conversion with -n succeeds with a target file ==
|
||||||
|
== Testing conversion to raw is the same after conversion with -n ==
|
||||||
|
== Testing conversion back to original format ==
|
||||||
|
No errors were found on the image.
|
||||||
|
== Testing conversion to a smaller file fails ==
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2097152
|
||||||
|
*** done
|
@ -78,50 +78,50 @@ _wrapup()
|
|||||||
|
|
||||||
if $showme
|
if $showme
|
||||||
then
|
then
|
||||||
:
|
:
|
||||||
elif $needwrap
|
elif $needwrap
|
||||||
then
|
then
|
||||||
if [ -f check.time -a -f $tmp.time ]
|
if [ -f check.time -a -f $tmp.time ]
|
||||||
then
|
then
|
||||||
cat check.time $tmp.time \
|
cat check.time $tmp.time \
|
||||||
| $AWK_PROG '
|
| $AWK_PROG '
|
||||||
{ t[$1] = $2 }
|
{ t[$1] = $2 }
|
||||||
END { if (NR > 0) {
|
END { if (NR > 0) {
|
||||||
for (i in t) print i " " t[i]
|
for (i in t) print i " " t[i]
|
||||||
}
|
}
|
||||||
}' \
|
}' \
|
||||||
| sort -n >$tmp.out
|
| sort -n >$tmp.out
|
||||||
mv $tmp.out check.time
|
mv $tmp.out check.time
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f $tmp.expunged ]
|
if [ -f $tmp.expunged ]
|
||||||
then
|
then
|
||||||
notrun=`wc -l <$tmp.expunged | sed -e 's/ *//g'`
|
notrun=`wc -l <$tmp.expunged | sed -e 's/ *//g'`
|
||||||
try=`expr $try - $notrun`
|
try=`expr $try - $notrun`
|
||||||
list=`echo "$list" | sed -f $tmp.expunged`
|
list=`echo "$list" | sed -f $tmp.expunged`
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "" >>check.log
|
echo "" >>check.log
|
||||||
date >>check.log
|
date >>check.log
|
||||||
echo $list | fmt | sed -e 's/^/ /' >>check.log
|
echo $list | fmt | sed -e 's/^/ /' >>check.log
|
||||||
$interrupt && echo "Interrupted!" >>check.log
|
$interrupt && echo "Interrupted!" >>check.log
|
||||||
|
|
||||||
if [ ! -z "$notrun" ]
|
if [ ! -z "$notrun" ]
|
||||||
then
|
then
|
||||||
echo "Not run:$notrun"
|
echo "Not run:$notrun"
|
||||||
echo "Not run:$notrun" >>check.log
|
echo "Not run:$notrun" >>check.log
|
||||||
fi
|
fi
|
||||||
if [ ! -z "$n_bad" -a $n_bad != 0 ]
|
if [ ! -z "$n_bad" -a $n_bad != 0 ]
|
||||||
then
|
then
|
||||||
echo "Failures:$bad"
|
echo "Failures:$bad"
|
||||||
echo "Failed $n_bad of $try tests"
|
echo "Failed $n_bad of $try tests"
|
||||||
echo "Failures:$bad" | fmt >>check.log
|
echo "Failures:$bad" | fmt >>check.log
|
||||||
echo "Failed $n_bad of $try tests" >>check.log
|
echo "Failed $n_bad of $try tests" >>check.log
|
||||||
else
|
else
|
||||||
echo "Passed all $try tests"
|
echo "Passed all $try tests"
|
||||||
echo "Passed all $try tests" >>check.log
|
echo "Passed all $try tests" >>check.log
|
||||||
fi
|
fi
|
||||||
needwrap=false
|
needwrap=false
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -f /tmp/*.out /tmp/*.err /tmp/*.time
|
rm -f /tmp/*.out /tmp/*.err /tmp/*.time
|
||||||
@ -185,82 +185,88 @@ do
|
|||||||
|
|
||||||
if $showme
|
if $showme
|
||||||
then
|
then
|
||||||
echo
|
echo
|
||||||
continue
|
continue
|
||||||
elif [ -f expunged ] && $expunge && egrep "^$seq([ ]|\$)" expunged >/dev/null
|
elif [ -f expunged ] && $expunge && egrep "^$seq([ ]|\$)" expunged >/dev/null
|
||||||
then
|
then
|
||||||
echo " - expunged"
|
echo " - expunged"
|
||||||
rm -f $seq.out.bad
|
rm -f $seq.out.bad
|
||||||
echo "/^$seq\$/d" >>$tmp.expunged
|
echo "/^$seq\$/d" >>$tmp.expunged
|
||||||
elif [ ! -f $seq ]
|
elif [ ! -f $seq ]
|
||||||
then
|
then
|
||||||
echo " - no such test?"
|
echo " - no such test?"
|
||||||
echo "/^$seq\$/d" >>$tmp.expunged
|
echo "/^$seq\$/d" >>$tmp.expunged
|
||||||
else
|
else
|
||||||
# really going to try and run this one
|
# really going to try and run this one
|
||||||
#
|
#
|
||||||
rm -f $seq.out.bad
|
rm -f $seq.out.bad
|
||||||
lasttime=`sed -n -e "/^$seq /s/.* //p" <check.time`
|
lasttime=`sed -n -e "/^$seq /s/.* //p" <check.time`
|
||||||
if [ "X$lasttime" != X ]; then
|
if [ "X$lasttime" != X ]; then
|
||||||
echo -n " ${lasttime}s ..."
|
echo -n " ${lasttime}s ..."
|
||||||
else
|
else
|
||||||
echo -n " " # prettier output with timestamps.
|
echo -n " " # prettier output with timestamps.
|
||||||
fi
|
fi
|
||||||
rm -f core $seq.notrun
|
rm -f core $seq.notrun
|
||||||
|
|
||||||
# for hangcheck ...
|
# for hangcheck ...
|
||||||
echo "$seq" >/tmp/check.sts
|
echo "$seq" >/tmp/check.sts
|
||||||
|
|
||||||
start=`_wallclock`
|
start=`_wallclock`
|
||||||
$timestamp && echo -n " ["`date "+%T"`"]"
|
$timestamp && echo -n " ["`date "+%T"`"]"
|
||||||
[ ! -x $seq ] && chmod u+x $seq # ensure we can run it
|
[ ! -x $seq ] && chmod u+x $seq # ensure we can run it
|
||||||
MALLOC_PERTURB_=${MALLOC_PERTURB_:-$(($RANDOM % 255 + 1))} \
|
MALLOC_PERTURB_=${MALLOC_PERTURB_:-$(($RANDOM % 255 + 1))} \
|
||||||
./$seq >$tmp.out 2>&1
|
./$seq >$tmp.out 2>&1
|
||||||
sts=$?
|
sts=$?
|
||||||
$timestamp && _timestamp
|
$timestamp && _timestamp
|
||||||
stop=`_wallclock`
|
stop=`_wallclock`
|
||||||
|
|
||||||
if [ -f core ]
|
if [ -f core ]
|
||||||
then
|
then
|
||||||
echo -n " [dumped core]"
|
echo -n " [dumped core]"
|
||||||
mv core $seq.core
|
mv core $seq.core
|
||||||
err=true
|
err=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -f $seq.notrun ]
|
if [ -f $seq.notrun ]
|
||||||
then
|
then
|
||||||
$timestamp || echo -n " [not run] "
|
$timestamp || echo -n " [not run] "
|
||||||
$timestamp && echo " [not run]" && echo -n " $seq -- "
|
$timestamp && echo " [not run]" && echo -n " $seq -- "
|
||||||
cat $seq.notrun
|
cat $seq.notrun
|
||||||
notrun="$notrun $seq"
|
notrun="$notrun $seq"
|
||||||
else
|
else
|
||||||
if [ $sts -ne 0 ]
|
if [ $sts -ne 0 ]
|
||||||
then
|
then
|
||||||
echo -n " [failed, exit status $sts]"
|
echo -n " [failed, exit status $sts]"
|
||||||
err=true
|
err=true
|
||||||
fi
|
fi
|
||||||
if [ ! -f $seq.out ]
|
|
||||||
then
|
reference=$seq.out
|
||||||
echo " - no qualified output"
|
if (echo $QEMU_IO_OPTIONS | grep -s -- '--nocache' > /dev/null); then
|
||||||
err=true
|
[ -f $seq.out.nocache ] && reference=$seq.out.nocache
|
||||||
else
|
fi
|
||||||
if diff -w $seq.out $tmp.out >/dev/null 2>&1
|
|
||||||
then
|
if [ ! -f $reference ]
|
||||||
echo ""
|
then
|
||||||
if $err
|
echo " - no qualified output"
|
||||||
then
|
err=true
|
||||||
:
|
else
|
||||||
else
|
if diff -w $reference $tmp.out >/dev/null 2>&1
|
||||||
echo "$seq `expr $stop - $start`" >>$tmp.time
|
then
|
||||||
fi
|
echo ""
|
||||||
else
|
if $err
|
||||||
echo " - output mismatch (see $seq.out.bad)"
|
then
|
||||||
mv $tmp.out $seq.out.bad
|
:
|
||||||
$diff -w $seq.out $seq.out.bad
|
else
|
||||||
err=true
|
echo "$seq `expr $stop - $start`" >>$tmp.time
|
||||||
fi
|
fi
|
||||||
fi
|
else
|
||||||
fi
|
echo " - output mismatch (see $seq.out.bad)"
|
||||||
|
mv $tmp.out $seq.out.bad
|
||||||
|
$diff -w $reference $seq.out.bad
|
||||||
|
err=true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -268,9 +274,9 @@ do
|
|||||||
#
|
#
|
||||||
if $err
|
if $err
|
||||||
then
|
then
|
||||||
bad="$bad $seq"
|
bad="$bad $seq"
|
||||||
n_bad=`expr $n_bad + 1`
|
n_bad=`expr $n_bad + 1`
|
||||||
quick=false
|
quick=false
|
||||||
fi
|
fi
|
||||||
[ -f $seq.notrun ] || try=`expr $try + 1`
|
[ -f $seq.notrun ] || try=`expr $try + 1`
|
||||||
|
|
||||||
|
@ -54,58 +54,58 @@ do
|
|||||||
|
|
||||||
if $group
|
if $group
|
||||||
then
|
then
|
||||||
# arg after -g
|
# arg after -g
|
||||||
group_list=`sed -n <group -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{
|
group_list=`sed -n <group -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{
|
||||||
s/ .*//p
|
s/ .*//p
|
||||||
}'`
|
}'`
|
||||||
if [ -z "$group_list" ]
|
if [ -z "$group_list" ]
|
||||||
then
|
then
|
||||||
echo "Group \"$r\" is empty or not defined?"
|
echo "Group \"$r\" is empty or not defined?"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
[ ! -s $tmp.list ] && touch $tmp.list
|
[ ! -s $tmp.list ] && touch $tmp.list
|
||||||
for t in $group_list
|
for t in $group_list
|
||||||
do
|
do
|
||||||
if grep -s "^$t\$" $tmp.list >/dev/null
|
if grep -s "^$t\$" $tmp.list >/dev/null
|
||||||
then
|
then
|
||||||
:
|
:
|
||||||
else
|
else
|
||||||
echo "$t" >>$tmp.list
|
echo "$t" >>$tmp.list
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
group=false
|
group=false
|
||||||
continue
|
continue
|
||||||
|
|
||||||
elif $xgroup
|
elif $xgroup
|
||||||
then
|
then
|
||||||
# arg after -x
|
# arg after -x
|
||||||
[ ! -s $tmp.list ] && ls [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] >$tmp.list 2>/dev/null
|
[ ! -s $tmp.list ] && ls [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] >$tmp.list 2>/dev/null
|
||||||
group_list=`sed -n <group -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{
|
group_list=`sed -n <group -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{
|
||||||
s/ .*//p
|
s/ .*//p
|
||||||
}'`
|
}'`
|
||||||
if [ -z "$group_list" ]
|
if [ -z "$group_list" ]
|
||||||
then
|
then
|
||||||
echo "Group \"$r\" is empty or not defined?"
|
echo "Group \"$r\" is empty or not defined?"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
numsed=0
|
numsed=0
|
||||||
rm -f $tmp.sed
|
rm -f $tmp.sed
|
||||||
for t in $group_list
|
for t in $group_list
|
||||||
do
|
do
|
||||||
if [ $numsed -gt 100 ]
|
if [ $numsed -gt 100 ]
|
||||||
then
|
then
|
||||||
sed -f $tmp.sed <$tmp.list >$tmp.tmp
|
sed -f $tmp.sed <$tmp.list >$tmp.tmp
|
||||||
mv $tmp.tmp $tmp.list
|
mv $tmp.tmp $tmp.list
|
||||||
numsed=0
|
numsed=0
|
||||||
rm -f $tmp.sed
|
rm -f $tmp.sed
|
||||||
fi
|
fi
|
||||||
echo "/^$t\$/d" >>$tmp.sed
|
echo "/^$t\$/d" >>$tmp.sed
|
||||||
numsed=`expr $numsed + 1`
|
numsed=`expr $numsed + 1`
|
||||||
done
|
done
|
||||||
sed -f $tmp.sed <$tmp.list >$tmp.tmp
|
sed -f $tmp.sed <$tmp.list >$tmp.tmp
|
||||||
mv $tmp.tmp $tmp.list
|
mv $tmp.tmp $tmp.list
|
||||||
xgroup=false
|
xgroup=false
|
||||||
continue
|
continue
|
||||||
|
|
||||||
elif $imgopts
|
elif $imgopts
|
||||||
then
|
then
|
||||||
@ -119,11 +119,11 @@ s/ .*//p
|
|||||||
case "$r"
|
case "$r"
|
||||||
in
|
in
|
||||||
|
|
||||||
-\? | -h | --help) # usage
|
-\? | -h | --help) # usage
|
||||||
echo "Usage: $0 [options] [testlist]"'
|
echo "Usage: $0 [options] [testlist]"'
|
||||||
|
|
||||||
common options
|
common options
|
||||||
-v verbose
|
-v verbose
|
||||||
|
|
||||||
check options
|
check options
|
||||||
-raw test raw (default)
|
-raw test raw (default)
|
||||||
@ -138,162 +138,162 @@ check options
|
|||||||
-sheepdog test sheepdog
|
-sheepdog test sheepdog
|
||||||
-nbd test nbd
|
-nbd test nbd
|
||||||
-ssh test ssh
|
-ssh test ssh
|
||||||
-xdiff graphical mode diff
|
-xdiff graphical mode diff
|
||||||
-nocache use O_DIRECT on backing file
|
-nocache use O_DIRECT on backing file
|
||||||
-misalign misalign memory allocations
|
-misalign misalign memory allocations
|
||||||
-n show me, do not run tests
|
-n show me, do not run tests
|
||||||
-o options -o options to pass to qemu-img create/convert
|
-o options -o options to pass to qemu-img create/convert
|
||||||
-T output timestamps
|
-T output timestamps
|
||||||
-r randomize test order
|
-r randomize test order
|
||||||
|
|
||||||
testlist options
|
testlist options
|
||||||
-g group[,group...] include tests from these groups
|
-g group[,group...] include tests from these groups
|
||||||
-x group[,group...] exclude tests from these groups
|
-x group[,group...] exclude tests from these groups
|
||||||
NNN include test NNN
|
NNN include test NNN
|
||||||
NNN-NNN include test range (eg. 012-021)
|
NNN-NNN include test range (eg. 012-021)
|
||||||
'
|
'
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-raw)
|
-raw)
|
||||||
IMGFMT=raw
|
IMGFMT=raw
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-cow)
|
-cow)
|
||||||
IMGFMT=cow
|
IMGFMT=cow
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-qcow)
|
-qcow)
|
||||||
IMGFMT=qcow
|
IMGFMT=qcow
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-qcow2)
|
-qcow2)
|
||||||
IMGFMT=qcow2
|
IMGFMT=qcow2
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-qed)
|
-qed)
|
||||||
IMGFMT=qed
|
IMGFMT=qed
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-vdi)
|
-vdi)
|
||||||
IMGFMT=vdi
|
IMGFMT=vdi
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-vmdk)
|
-vmdk)
|
||||||
IMGFMT=vmdk
|
IMGFMT=vmdk
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-vpc)
|
-vpc)
|
||||||
IMGFMT=vpc
|
IMGFMT=vpc
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-rbd)
|
-rbd)
|
||||||
IMGPROTO=rbd
|
IMGPROTO=rbd
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
-sheepdog)
|
-sheepdog)
|
||||||
IMGPROTO=sheepdog
|
IMGPROTO=sheepdog
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
-nbd)
|
-nbd)
|
||||||
IMGPROTO=nbd
|
IMGPROTO=nbd
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
-ssh)
|
-ssh)
|
||||||
IMGPROTO=ssh
|
IMGPROTO=ssh
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
-nocache)
|
-nocache)
|
||||||
QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --nocache"
|
QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --nocache"
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-misalign)
|
-misalign)
|
||||||
QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --misalign"
|
QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS --misalign"
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-valgrind)
|
-valgrind)
|
||||||
valgrind=true
|
valgrind=true
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-g) # -g group ... pick from group file
|
-g) # -g group ... pick from group file
|
||||||
group=true
|
group=true
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-xdiff) # graphical diff mode
|
-xdiff) # graphical diff mode
|
||||||
xpand=false
|
xpand=false
|
||||||
|
|
||||||
if [ ! -z "$DISPLAY" ]
|
if [ ! -z "$DISPLAY" ]
|
||||||
then
|
then
|
||||||
which xdiff >/dev/null 2>&1 && diff=xdiff
|
which xdiff >/dev/null 2>&1 && diff=xdiff
|
||||||
which gdiff >/dev/null 2>&1 && diff=gdiff
|
which gdiff >/dev/null 2>&1 && diff=gdiff
|
||||||
which tkdiff >/dev/null 2>&1 && diff=tkdiff
|
which tkdiff >/dev/null 2>&1 && diff=tkdiff
|
||||||
which xxdiff >/dev/null 2>&1 && diff=xxdiff
|
which xxdiff >/dev/null 2>&1 && diff=xxdiff
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-n) # show me, don't do it
|
-n) # show me, don't do it
|
||||||
showme=true
|
showme=true
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
-o)
|
-o)
|
||||||
imgopts=true
|
imgopts=true
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
-r) # randomize test order
|
-r) # randomize test order
|
||||||
randomize=true
|
randomize=true
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-T) # turn on timestamp output
|
-T) # turn on timestamp output
|
||||||
timestamp=true
|
timestamp=true
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
|
|
||||||
-v)
|
-v)
|
||||||
verbose=true
|
verbose=true
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
-x) # -x group ... exclude from group file
|
-x) # -x group ... exclude from group file
|
||||||
xgroup=true
|
xgroup=true
|
||||||
xpand=false
|
xpand=false
|
||||||
;;
|
;;
|
||||||
'[0-9][0-9][0-9] [0-9][0-9][0-9][0-9]')
|
'[0-9][0-9][0-9] [0-9][0-9][0-9][0-9]')
|
||||||
echo "No tests?"
|
echo "No tests?"
|
||||||
status=1
|
status=1
|
||||||
exit $status
|
exit $status
|
||||||
;;
|
;;
|
||||||
|
|
||||||
[0-9]*-[0-9]*)
|
[0-9]*-[0-9]*)
|
||||||
eval `echo $r | sed -e 's/^/start=/' -e 's/-/ end=/'`
|
eval `echo $r | sed -e 's/^/start=/' -e 's/-/ end=/'`
|
||||||
;;
|
;;
|
||||||
|
|
||||||
[0-9]*-)
|
[0-9]*-)
|
||||||
eval `echo $r | sed -e 's/^/start=/' -e 's/-//'`
|
eval `echo $r | sed -e 's/^/start=/' -e 's/-//'`
|
||||||
end=`echo [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] | sed -e 's/\[0-9]//g' -e 's/ *$//' -e 's/.* //'`
|
end=`echo [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] | sed -e 's/\[0-9]//g' -e 's/ *$//' -e 's/.* //'`
|
||||||
if [ -z "$end" ]
|
if [ -z "$end" ]
|
||||||
then
|
then
|
||||||
echo "No tests in range \"$r\"?"
|
echo "No tests in range \"$r\"?"
|
||||||
status=1
|
status=1
|
||||||
exit $status
|
exit $status
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
start=$r
|
start=$r
|
||||||
end=$r
|
end=$r
|
||||||
;;
|
;;
|
||||||
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
@ -303,26 +303,26 @@ testlist options
|
|||||||
|
|
||||||
if $xpand
|
if $xpand
|
||||||
then
|
then
|
||||||
have_test_arg=true
|
have_test_arg=true
|
||||||
$AWK_PROG </dev/null '
|
$AWK_PROG </dev/null '
|
||||||
BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \
|
BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \
|
||||||
| while read id
|
| while read id
|
||||||
do
|
do
|
||||||
if grep -s "^$id " group >/dev/null
|
if grep -s "^$id " group >/dev/null
|
||||||
then
|
then
|
||||||
# in group file ... OK
|
# in group file ... OK
|
||||||
echo $id >>$tmp.list
|
echo $id >>$tmp.list
|
||||||
else
|
else
|
||||||
if [ -f expunged ] && $expunge && egrep "^$id([ ]|\$)" expunged >/dev/null
|
if [ -f expunged ] && $expunge && egrep "^$id([ ]|\$)" expunged >/dev/null
|
||||||
then
|
then
|
||||||
# expunged ... will be reported, but not run, later
|
# expunged ... will be reported, but not run, later
|
||||||
echo $id >>$tmp.list
|
echo $id >>$tmp.list
|
||||||
else
|
else
|
||||||
# oops
|
# oops
|
||||||
echo "$id - unknown test, ignored"
|
echo "$id - unknown test, ignored"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
done
|
done
|
||||||
@ -337,11 +337,11 @@ then
|
|||||||
else
|
else
|
||||||
if $have_test_arg
|
if $have_test_arg
|
||||||
then
|
then
|
||||||
# had test numbers, but none in group file ... do nothing
|
# had test numbers, but none in group file ... do nothing
|
||||||
touch $tmp.list
|
touch $tmp.list
|
||||||
else
|
else
|
||||||
# no test numbers, do everything from group file
|
# no test numbers, do everything from group file
|
||||||
sed -n -e '/^[0-9][0-9][0-9]*/s/[ ].*//p' <group >$tmp.list
|
sed -n -e '/^[0-9][0-9][0-9]*/s/[ ].*//p' <group >$tmp.list
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -111,11 +111,11 @@ export QEMU_NBD=$QEMU_NBD_PROG
|
|||||||
[ -f /etc/qemu-iotest.config ] && . /etc/qemu-iotest.config
|
[ -f /etc/qemu-iotest.config ] && . /etc/qemu-iotest.config
|
||||||
|
|
||||||
if [ -z "$TEST_DIR" ]; then
|
if [ -z "$TEST_DIR" ]; then
|
||||||
TEST_DIR=`pwd`/scratch
|
TEST_DIR=`pwd`/scratch
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -e "$TEST_DIR" ]; then
|
if [ ! -e "$TEST_DIR" ]; then
|
||||||
mkdir "$TEST_DIR"
|
mkdir "$TEST_DIR"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -d "$TEST_DIR" ]; then
|
if [ ! -d "$TEST_DIR" ]; then
|
||||||
|
@ -54,7 +54,7 @@ _within_tolerance()
|
|||||||
then
|
then
|
||||||
if [ "$5" = "-v" ]
|
if [ "$5" = "-v" ]
|
||||||
then
|
then
|
||||||
_verbose=1
|
_verbose=1
|
||||||
else
|
else
|
||||||
_maxtol=$5
|
_maxtol=$5
|
||||||
fi
|
fi
|
||||||
@ -111,12 +111,12 @@ EOF
|
|||||||
|
|
||||||
if [ $_in_range -eq 1 ]
|
if [ $_in_range -eq 1 ]
|
||||||
then
|
then
|
||||||
[ $_verbose -eq 1 ] && echo $_name is in range
|
[ $_verbose -eq 1 ] && echo $_name is in range
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
[ $_verbose -eq 1 ] && echo $_name has value of $_given_val
|
[ $_verbose -eq 1 ] && echo $_name has value of $_given_val
|
||||||
[ $_verbose -eq 1 ] && echo $_name is NOT in range $_min .. $_max
|
[ $_verbose -eq 1 ] && echo $_name is NOT in range $_min .. $_max
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ EOF
|
|||||||
_filter_date()
|
_filter_date()
|
||||||
{
|
{
|
||||||
sed \
|
sed \
|
||||||
-e 's/[A-Z][a-z][a-z] [A-z][a-z][a-z] *[0-9][0-9]* [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9][0-9][0-9][0-9]$/DATE/'
|
-e 's/[A-Z][a-z][a-z] [A-z][a-z][a-z] *[0-9][0-9]* [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9][0-9][0-9][0-9]$/DATE/'
|
||||||
}
|
}
|
||||||
|
|
||||||
# replace occurrences of the actual TEST_DIR value with TEST_DIR
|
# replace occurrences of the actual TEST_DIR value with TEST_DIR
|
||||||
|
@ -106,8 +106,8 @@ function io_test2() {
|
|||||||
local num=$3
|
local num=$3
|
||||||
|
|
||||||
# Pattern (repeat after 9 clusters):
|
# Pattern (repeat after 9 clusters):
|
||||||
# used - used - free - used - compressed - compressed -
|
# used - used - free - used - compressed - compressed -
|
||||||
# free - free - compressed
|
# free - free - compressed
|
||||||
|
|
||||||
# Write the clusters to be compressed
|
# Write the clusters to be compressed
|
||||||
echo === Clusters to be compressed [1]
|
echo === Clusters to be compressed [1]
|
||||||
|
@ -21,16 +21,16 @@ dd()
|
|||||||
{
|
{
|
||||||
if [ "$HOSTOS" == "Linux" ]
|
if [ "$HOSTOS" == "Linux" ]
|
||||||
then
|
then
|
||||||
command dd --help | grep noxfer > /dev/null 2>&1
|
command dd --help | grep noxfer > /dev/null 2>&1
|
||||||
|
|
||||||
if [ "$?" -eq 0 ]
|
if [ "$?" -eq 0 ]
|
||||||
then
|
then
|
||||||
command dd status=noxfer $@
|
command dd status=noxfer $@
|
||||||
else
|
else
|
||||||
command dd $@
|
command dd $@
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
command dd $@
|
command dd $@
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,8 +193,8 @@ _get_pids_by_name()
|
|||||||
{
|
{
|
||||||
if [ $# -ne 1 ]
|
if [ $# -ne 1 ]
|
||||||
then
|
then
|
||||||
echo "Usage: _get_pids_by_name process-name" 1>&2
|
echo "Usage: _get_pids_by_name process-name" 1>&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Algorithm ... all ps(1) variants have a time of the form MM:SS or
|
# Algorithm ... all ps(1) variants have a time of the form MM:SS or
|
||||||
@ -206,12 +206,12 @@ _get_pids_by_name()
|
|||||||
|
|
||||||
ps $PS_ALL_FLAGS \
|
ps $PS_ALL_FLAGS \
|
||||||
| sed -n \
|
| sed -n \
|
||||||
-e 's/$/ /' \
|
-e 's/$/ /' \
|
||||||
-e 's/[ ][ ]*/ /g' \
|
-e 's/[ ][ ]*/ /g' \
|
||||||
-e 's/^ //' \
|
-e 's/^ //' \
|
||||||
-e 's/^[^ ]* //' \
|
-e 's/^[^ ]* //' \
|
||||||
-e "/[0-9]:[0-9][0-9] *[^ ]*\/$1 /s/ .*//p" \
|
-e "/[0-9]:[0-9][0-9] *[^ ]*\/$1 /s/ .*//p" \
|
||||||
-e "/[0-9]:[0-9][0-9] *$1 /s/ .*//p"
|
-e "/[0-9]:[0-9][0-9] *$1 /s/ .*//p"
|
||||||
}
|
}
|
||||||
|
|
||||||
# fqdn for localhost
|
# fqdn for localhost
|
||||||
@ -229,8 +229,8 @@ _need_to_be_root()
|
|||||||
id=`id | $SED_PROG -e 's/(.*//' -e 's/.*=//'`
|
id=`id | $SED_PROG -e 's/(.*//' -e 's/.*=//'`
|
||||||
if [ "$id" -ne 0 ]
|
if [ "$id" -ne 0 ]
|
||||||
then
|
then
|
||||||
echo "Arrgh ... you need to be root (not uid=$id) to run this test"
|
echo "Arrgh ... you need to be root (not uid=$id) to run this test"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,33 +248,33 @@ _need_to_be_root()
|
|||||||
_do()
|
_do()
|
||||||
{
|
{
|
||||||
if [ $# -eq 1 ]; then
|
if [ $# -eq 1 ]; then
|
||||||
_cmd=$1
|
_cmd=$1
|
||||||
elif [ $# -eq 2 ]; then
|
elif [ $# -eq 2 ]; then
|
||||||
_note=$1
|
_note=$1
|
||||||
_cmd=$2
|
_cmd=$2
|
||||||
echo -n "$_note... "
|
echo -n "$_note... "
|
||||||
else
|
else
|
||||||
echo "Usage: _do [note] cmd" 1>&2
|
echo "Usage: _do [note] cmd" 1>&2
|
||||||
status=1; exit
|
status=1; exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
(eval "echo '---' \"$_cmd\"") >>$here/$seq.full
|
(eval "echo '---' \"$_cmd\"") >>$here/$seq.full
|
||||||
(eval "$_cmd") >$tmp._out 2>&1; ret=$?
|
(eval "$_cmd") >$tmp._out 2>&1; ret=$?
|
||||||
cat $tmp._out >>$here/$seq.full
|
cat $tmp._out >>$here/$seq.full
|
||||||
if [ $# -eq 2 ]; then
|
if [ $# -eq 2 ]; then
|
||||||
if [ $ret -eq 0 ]; then
|
if [ $ret -eq 0 ]; then
|
||||||
echo "done"
|
echo "done"
|
||||||
else
|
else
|
||||||
echo "fail"
|
echo "fail"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
if [ $ret -ne 0 ] \
|
if [ $ret -ne 0 ] \
|
||||||
&& [ "$_do_die_on_error" = "always" \
|
&& [ "$_do_die_on_error" = "always" \
|
||||||
-o \( $# -eq 2 -a "$_do_die_on_error" = "message_only" \) ]
|
-o \( $# -eq 2 -a "$_do_die_on_error" = "message_only" \) ]
|
||||||
then
|
then
|
||||||
[ $# -ne 2 ] && echo
|
[ $# -ne 2 ] && echo
|
||||||
eval "echo \"$_cmd\" failed \(returned $ret\): see $seq.full"
|
eval "echo \"$_cmd\" failed \(returned $ret\): see $seq.full"
|
||||||
status=1; exit
|
status=1; exit
|
||||||
fi
|
fi
|
||||||
|
|
||||||
return $ret
|
return $ret
|
||||||
@ -305,9 +305,9 @@ _fail()
|
|||||||
_supported_fmt()
|
_supported_fmt()
|
||||||
{
|
{
|
||||||
for f; do
|
for f; do
|
||||||
if [ "$f" = "$IMGFMT" -o "$f" = "generic" ]; then
|
if [ "$f" = "$IMGFMT" -o "$f" = "generic" ]; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
_notrun "not suitable for this image format: $IMGFMT"
|
_notrun "not suitable for this image format: $IMGFMT"
|
||||||
@ -318,9 +318,9 @@ _supported_fmt()
|
|||||||
_supported_proto()
|
_supported_proto()
|
||||||
{
|
{
|
||||||
for f; do
|
for f; do
|
||||||
if [ "$f" = "$IMGPROTO" -o "$f" = "generic" ]; then
|
if [ "$f" = "$IMGPROTO" -o "$f" = "generic" ]; then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
_notrun "not suitable for this image protocol: $IMGPROTO"
|
_notrun "not suitable for this image protocol: $IMGPROTO"
|
||||||
@ -332,10 +332,10 @@ _supported_os()
|
|||||||
{
|
{
|
||||||
for h
|
for h
|
||||||
do
|
do
|
||||||
if [ "$h" = "$HOSTOS" ]
|
if [ "$h" = "$HOSTOS" ]
|
||||||
then
|
then
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
_notrun "not suitable for this OS: $HOSTOS"
|
_notrun "not suitable for this OS: $HOSTOS"
|
||||||
|
@ -66,3 +66,4 @@
|
|||||||
059 rw auto
|
059 rw auto
|
||||||
060 rw auto
|
060 rw auto
|
||||||
062 rw auto
|
062 rw auto
|
||||||
|
063 rw auto
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include "block/aio.h"
|
#include "block/aio.h"
|
||||||
#include "qemu/timer.h"
|
#include "qemu/timer.h"
|
||||||
|
#include "qemu/sockets.h"
|
||||||
|
|
||||||
AioContext *ctx;
|
AioContext *ctx;
|
||||||
|
|
||||||
@ -375,7 +376,10 @@ static void test_timer_schedule(void)
|
|||||||
/* aio_poll will not block to wait for timers to complete unless it has
|
/* aio_poll will not block to wait for timers to complete unless it has
|
||||||
* an fd to wait on. Fixing this breaks other tests. So create a dummy one.
|
* an fd to wait on. Fixing this breaks other tests. So create a dummy one.
|
||||||
*/
|
*/
|
||||||
g_assert(!pipe2(pipefd, O_NONBLOCK));
|
g_assert(!qemu_pipe(pipefd));
|
||||||
|
qemu_set_nonblock(pipefd[0]);
|
||||||
|
qemu_set_nonblock(pipefd[1]);
|
||||||
|
|
||||||
aio_set_fd_handler(ctx, pipefd[0],
|
aio_set_fd_handler(ctx, pipefd[0],
|
||||||
dummy_io_handler_read, NULL, NULL);
|
dummy_io_handler_read, NULL, NULL);
|
||||||
aio_poll(ctx, false);
|
aio_poll(ctx, false);
|
||||||
@ -716,7 +720,10 @@ static void test_source_timer_schedule(void)
|
|||||||
/* aio_poll will not block to wait for timers to complete unless it has
|
/* aio_poll will not block to wait for timers to complete unless it has
|
||||||
* an fd to wait on. Fixing this breaks other tests. So create a dummy one.
|
* an fd to wait on. Fixing this breaks other tests. So create a dummy one.
|
||||||
*/
|
*/
|
||||||
g_assert(!pipe2(pipefd, O_NONBLOCK));
|
g_assert(!qemu_pipe(pipefd));
|
||||||
|
qemu_set_nonblock(pipefd[0]);
|
||||||
|
qemu_set_nonblock(pipefd[1]);
|
||||||
|
|
||||||
aio_set_fd_handler(ctx, pipefd[0],
|
aio_set_fd_handler(ctx, pipefd[0],
|
||||||
dummy_io_handler_read, NULL, NULL);
|
dummy_io_handler_read, NULL, NULL);
|
||||||
do {} while (g_main_context_iteration(NULL, false));
|
do {} while (g_main_context_iteration(NULL, false));
|
||||||
|
481
tests/test-throttle.c
Normal file
481
tests/test-throttle.c
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
/*
|
||||||
|
* Throttle infrastructure tests
|
||||||
|
*
|
||||||
|
* Copyright Nodalink, SARL. 2013
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Benoît Canet <benoit.canet@irqsave.net>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
||||||
|
* See the COPYING.LIB file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "qemu/throttle.h"
|
||||||
|
|
||||||
|
LeakyBucket bkt;
|
||||||
|
ThrottleConfig cfg;
|
||||||
|
ThrottleState ts;
|
||||||
|
|
||||||
|
/* usefull function */
|
||||||
|
static bool double_cmp(double x, double y)
|
||||||
|
{
|
||||||
|
return fabsl(x - y) < 1e-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tests for single bucket operations */
|
||||||
|
static void test_leak_bucket(void)
|
||||||
|
{
|
||||||
|
/* set initial value */
|
||||||
|
bkt.avg = 150;
|
||||||
|
bkt.max = 15;
|
||||||
|
bkt.level = 1.5;
|
||||||
|
|
||||||
|
/* leak an op work of time */
|
||||||
|
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
|
||||||
|
g_assert(bkt.avg == 150);
|
||||||
|
g_assert(bkt.max == 15);
|
||||||
|
g_assert(double_cmp(bkt.level, 0.5));
|
||||||
|
|
||||||
|
/* leak again emptying the bucket */
|
||||||
|
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
|
||||||
|
g_assert(bkt.avg == 150);
|
||||||
|
g_assert(bkt.max == 15);
|
||||||
|
g_assert(double_cmp(bkt.level, 0));
|
||||||
|
|
||||||
|
/* check that the bucket level won't go lower */
|
||||||
|
throttle_leak_bucket(&bkt, NANOSECONDS_PER_SECOND / 150);
|
||||||
|
g_assert(bkt.avg == 150);
|
||||||
|
g_assert(bkt.max == 15);
|
||||||
|
g_assert(double_cmp(bkt.level, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_compute_wait(void)
|
||||||
|
{
|
||||||
|
int64_t wait;
|
||||||
|
int64_t result;
|
||||||
|
|
||||||
|
/* no operation limit set */
|
||||||
|
bkt.avg = 0;
|
||||||
|
bkt.max = 15;
|
||||||
|
bkt.level = 1.5;
|
||||||
|
wait = throttle_compute_wait(&bkt);
|
||||||
|
g_assert(!wait);
|
||||||
|
|
||||||
|
/* zero delta */
|
||||||
|
bkt.avg = 150;
|
||||||
|
bkt.max = 15;
|
||||||
|
bkt.level = 15;
|
||||||
|
wait = throttle_compute_wait(&bkt);
|
||||||
|
g_assert(!wait);
|
||||||
|
|
||||||
|
/* below zero delta */
|
||||||
|
bkt.avg = 150;
|
||||||
|
bkt.max = 15;
|
||||||
|
bkt.level = 9;
|
||||||
|
wait = throttle_compute_wait(&bkt);
|
||||||
|
g_assert(!wait);
|
||||||
|
|
||||||
|
/* half an operation above max */
|
||||||
|
bkt.avg = 150;
|
||||||
|
bkt.max = 15;
|
||||||
|
bkt.level = 15.5;
|
||||||
|
wait = throttle_compute_wait(&bkt);
|
||||||
|
/* time required to do half an operation */
|
||||||
|
result = (int64_t) NANOSECONDS_PER_SECOND / 150 / 2;
|
||||||
|
g_assert(wait == result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* functions to test ThrottleState initialization/destroy methods */
|
||||||
|
static void read_timer_cb(void *opaque)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_timer_cb(void *opaque)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* fill the structure with crap */
|
||||||
|
memset(&ts, 1, sizeof(ts));
|
||||||
|
|
||||||
|
/* init the structure */
|
||||||
|
throttle_init(&ts, QEMU_CLOCK_VIRTUAL, read_timer_cb, write_timer_cb, &ts);
|
||||||
|
|
||||||
|
/* check initialized fields */
|
||||||
|
g_assert(ts.clock_type == QEMU_CLOCK_VIRTUAL);
|
||||||
|
g_assert(ts.timers[0]);
|
||||||
|
g_assert(ts.timers[1]);
|
||||||
|
|
||||||
|
/* check other fields where cleared */
|
||||||
|
g_assert(!ts.previous_leak);
|
||||||
|
g_assert(!ts.cfg.op_size);
|
||||||
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
||||||
|
g_assert(!ts.cfg.buckets[i].avg);
|
||||||
|
g_assert(!ts.cfg.buckets[i].max);
|
||||||
|
g_assert(!ts.cfg.buckets[i].level);
|
||||||
|
}
|
||||||
|
|
||||||
|
throttle_destroy(&ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_destroy(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
throttle_init(&ts, QEMU_CLOCK_VIRTUAL, read_timer_cb, write_timer_cb, &ts);
|
||||||
|
throttle_destroy(&ts);
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
g_assert(!ts.timers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* function to test throttle_config and throttle_get_config */
|
||||||
|
static void test_config_functions(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
ThrottleConfig orig_cfg, final_cfg;
|
||||||
|
|
||||||
|
orig_cfg.buckets[THROTTLE_BPS_TOTAL].avg = 153;
|
||||||
|
orig_cfg.buckets[THROTTLE_BPS_READ].avg = 56;
|
||||||
|
orig_cfg.buckets[THROTTLE_BPS_WRITE].avg = 1;
|
||||||
|
|
||||||
|
orig_cfg.buckets[THROTTLE_OPS_TOTAL].avg = 150;
|
||||||
|
orig_cfg.buckets[THROTTLE_OPS_READ].avg = 69;
|
||||||
|
orig_cfg.buckets[THROTTLE_OPS_WRITE].avg = 23;
|
||||||
|
|
||||||
|
orig_cfg.buckets[THROTTLE_BPS_TOTAL].max = 0; /* should be corrected */
|
||||||
|
orig_cfg.buckets[THROTTLE_BPS_READ].max = 1; /* should not be corrected */
|
||||||
|
orig_cfg.buckets[THROTTLE_BPS_WRITE].max = 120;
|
||||||
|
|
||||||
|
orig_cfg.buckets[THROTTLE_OPS_TOTAL].max = 150;
|
||||||
|
orig_cfg.buckets[THROTTLE_OPS_READ].max = 400;
|
||||||
|
orig_cfg.buckets[THROTTLE_OPS_WRITE].max = 500;
|
||||||
|
|
||||||
|
orig_cfg.buckets[THROTTLE_BPS_TOTAL].level = 45;
|
||||||
|
orig_cfg.buckets[THROTTLE_BPS_READ].level = 65;
|
||||||
|
orig_cfg.buckets[THROTTLE_BPS_WRITE].level = 23;
|
||||||
|
|
||||||
|
orig_cfg.buckets[THROTTLE_OPS_TOTAL].level = 1;
|
||||||
|
orig_cfg.buckets[THROTTLE_OPS_READ].level = 90;
|
||||||
|
orig_cfg.buckets[THROTTLE_OPS_WRITE].level = 75;
|
||||||
|
|
||||||
|
orig_cfg.op_size = 1;
|
||||||
|
|
||||||
|
throttle_init(&ts, QEMU_CLOCK_VIRTUAL, read_timer_cb, write_timer_cb, &ts);
|
||||||
|
/* structure reset by throttle_init previous_leak should be null */
|
||||||
|
g_assert(!ts.previous_leak);
|
||||||
|
throttle_config(&ts, &orig_cfg);
|
||||||
|
|
||||||
|
/* has previous leak been initialized by throttle_config ? */
|
||||||
|
g_assert(ts.previous_leak);
|
||||||
|
|
||||||
|
/* get back the fixed configuration */
|
||||||
|
throttle_get_config(&ts, &final_cfg);
|
||||||
|
|
||||||
|
throttle_destroy(&ts);
|
||||||
|
|
||||||
|
g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153);
|
||||||
|
g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56);
|
||||||
|
g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].avg == 1);
|
||||||
|
|
||||||
|
g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].avg == 150);
|
||||||
|
g_assert(final_cfg.buckets[THROTTLE_OPS_READ].avg == 69);
|
||||||
|
g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].avg == 23);
|
||||||
|
|
||||||
|
g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].max == 15.3);/* fixed */
|
||||||
|
g_assert(final_cfg.buckets[THROTTLE_BPS_READ].max == 1); /* not fixed */
|
||||||
|
g_assert(final_cfg.buckets[THROTTLE_BPS_WRITE].max == 120);
|
||||||
|
|
||||||
|
g_assert(final_cfg.buckets[THROTTLE_OPS_TOTAL].max == 150);
|
||||||
|
g_assert(final_cfg.buckets[THROTTLE_OPS_READ].max == 400);
|
||||||
|
g_assert(final_cfg.buckets[THROTTLE_OPS_WRITE].max == 500);
|
||||||
|
|
||||||
|
g_assert(final_cfg.op_size == 1);
|
||||||
|
|
||||||
|
/* check bucket have been cleared */
|
||||||
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
||||||
|
g_assert(!final_cfg.buckets[i].level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* functions to test is throttle is enabled by a config */
|
||||||
|
static void set_cfg_value(bool is_max, int index, int value)
|
||||||
|
{
|
||||||
|
if (is_max) {
|
||||||
|
cfg.buckets[index].max = value;
|
||||||
|
} else {
|
||||||
|
cfg.buckets[index].avg = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_enabled(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
g_assert(!throttle_enabled(&cfg));
|
||||||
|
|
||||||
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
set_cfg_value(false, i, 150);
|
||||||
|
g_assert(throttle_enabled(&cfg));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
set_cfg_value(false, i, -150);
|
||||||
|
g_assert(!throttle_enabled(&cfg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tests functions for throttle_conflicting */
|
||||||
|
|
||||||
|
static void test_conflicts_for_one_set(bool is_max,
|
||||||
|
int total,
|
||||||
|
int read,
|
||||||
|
int write)
|
||||||
|
{
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
g_assert(!throttle_conflicting(&cfg));
|
||||||
|
|
||||||
|
set_cfg_value(is_max, total, 1);
|
||||||
|
set_cfg_value(is_max, read, 1);
|
||||||
|
g_assert(throttle_conflicting(&cfg));
|
||||||
|
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
set_cfg_value(is_max, total, 1);
|
||||||
|
set_cfg_value(is_max, write, 1);
|
||||||
|
g_assert(throttle_conflicting(&cfg));
|
||||||
|
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
set_cfg_value(is_max, total, 1);
|
||||||
|
set_cfg_value(is_max, read, 1);
|
||||||
|
set_cfg_value(is_max, write, 1);
|
||||||
|
g_assert(throttle_conflicting(&cfg));
|
||||||
|
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
set_cfg_value(is_max, total, 1);
|
||||||
|
g_assert(!throttle_conflicting(&cfg));
|
||||||
|
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
set_cfg_value(is_max, read, 1);
|
||||||
|
set_cfg_value(is_max, write, 1);
|
||||||
|
g_assert(!throttle_conflicting(&cfg));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_conflicting_config(void)
|
||||||
|
{
|
||||||
|
/* bps average conflicts */
|
||||||
|
test_conflicts_for_one_set(false,
|
||||||
|
THROTTLE_BPS_TOTAL,
|
||||||
|
THROTTLE_BPS_READ,
|
||||||
|
THROTTLE_BPS_WRITE);
|
||||||
|
|
||||||
|
/* ops average conflicts */
|
||||||
|
test_conflicts_for_one_set(false,
|
||||||
|
THROTTLE_OPS_TOTAL,
|
||||||
|
THROTTLE_OPS_READ,
|
||||||
|
THROTTLE_OPS_WRITE);
|
||||||
|
|
||||||
|
/* bps average conflicts */
|
||||||
|
test_conflicts_for_one_set(true,
|
||||||
|
THROTTLE_BPS_TOTAL,
|
||||||
|
THROTTLE_BPS_READ,
|
||||||
|
THROTTLE_BPS_WRITE);
|
||||||
|
/* ops average conflicts */
|
||||||
|
test_conflicts_for_one_set(true,
|
||||||
|
THROTTLE_OPS_TOTAL,
|
||||||
|
THROTTLE_OPS_READ,
|
||||||
|
THROTTLE_OPS_WRITE);
|
||||||
|
}
|
||||||
|
/* functions to test the throttle_is_valid function */
|
||||||
|
static void test_is_valid_for_value(int value, bool should_be_valid)
|
||||||
|
{
|
||||||
|
int is_max, index;
|
||||||
|
for (is_max = 0; is_max < 2; is_max++) {
|
||||||
|
for (index = 0; index < BUCKETS_COUNT; index++) {
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
set_cfg_value(is_max, index, value);
|
||||||
|
g_assert(throttle_is_valid(&cfg) == should_be_valid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_is_valid(void)
|
||||||
|
{
|
||||||
|
/* negative number are invalid */
|
||||||
|
test_is_valid_for_value(-1, false);
|
||||||
|
/* zero are valids */
|
||||||
|
test_is_valid_for_value(0, true);
|
||||||
|
/* positives numers are valids */
|
||||||
|
test_is_valid_for_value(1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_have_timer(void)
|
||||||
|
{
|
||||||
|
/* zero the structure */
|
||||||
|
memset(&ts, 0, sizeof(ts));
|
||||||
|
|
||||||
|
/* no timer set shoudl return false */
|
||||||
|
g_assert(!throttle_have_timer(&ts));
|
||||||
|
|
||||||
|
/* init the structure */
|
||||||
|
throttle_init(&ts, QEMU_CLOCK_VIRTUAL, read_timer_cb, write_timer_cb, &ts);
|
||||||
|
|
||||||
|
/* timer set by init should return true */
|
||||||
|
g_assert(throttle_have_timer(&ts));
|
||||||
|
|
||||||
|
throttle_destroy(&ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */
|
||||||
|
int size, /* size of the operation to do */
|
||||||
|
double avg, /* io limit */
|
||||||
|
uint64_t op_size, /* ideal size of an io */
|
||||||
|
double total_result,
|
||||||
|
double read_result,
|
||||||
|
double write_result)
|
||||||
|
{
|
||||||
|
BucketType to_test[2][3] = { { THROTTLE_BPS_TOTAL,
|
||||||
|
THROTTLE_BPS_READ,
|
||||||
|
THROTTLE_BPS_WRITE, },
|
||||||
|
{ THROTTLE_OPS_TOTAL,
|
||||||
|
THROTTLE_OPS_READ,
|
||||||
|
THROTTLE_OPS_WRITE, } };
|
||||||
|
ThrottleConfig cfg;
|
||||||
|
BucketType index;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 3; i++) {
|
||||||
|
BucketType index = to_test[is_ops][i];
|
||||||
|
cfg.buckets[index].avg = avg;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.op_size = op_size;
|
||||||
|
|
||||||
|
throttle_init(&ts, QEMU_CLOCK_VIRTUAL, read_timer_cb, write_timer_cb, &ts);
|
||||||
|
throttle_config(&ts, &cfg);
|
||||||
|
|
||||||
|
/* account a read */
|
||||||
|
throttle_account(&ts, false, size);
|
||||||
|
/* account a write */
|
||||||
|
throttle_account(&ts, true, size);
|
||||||
|
|
||||||
|
/* check total result */
|
||||||
|
index = to_test[is_ops][0];
|
||||||
|
if (!double_cmp(ts.cfg.buckets[index].level, total_result)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check read result */
|
||||||
|
index = to_test[is_ops][1];
|
||||||
|
if (!double_cmp(ts.cfg.buckets[index].level, read_result)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check write result */
|
||||||
|
index = to_test[is_ops][2];
|
||||||
|
if (!double_cmp(ts.cfg.buckets[index].level, write_result)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
throttle_destroy(&ts);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_accounting(void)
|
||||||
|
{
|
||||||
|
/* tests for bps */
|
||||||
|
|
||||||
|
/* op of size 1 */
|
||||||
|
g_assert(do_test_accounting(false,
|
||||||
|
1 * 512,
|
||||||
|
150,
|
||||||
|
0,
|
||||||
|
1024,
|
||||||
|
512,
|
||||||
|
512));
|
||||||
|
|
||||||
|
/* op of size 2 */
|
||||||
|
g_assert(do_test_accounting(false,
|
||||||
|
2 * 512,
|
||||||
|
150,
|
||||||
|
0,
|
||||||
|
2048,
|
||||||
|
1024,
|
||||||
|
1024));
|
||||||
|
|
||||||
|
/* op of size 2 and orthogonal parameter change */
|
||||||
|
g_assert(do_test_accounting(false,
|
||||||
|
2 * 512,
|
||||||
|
150,
|
||||||
|
17,
|
||||||
|
2048,
|
||||||
|
1024,
|
||||||
|
1024));
|
||||||
|
|
||||||
|
|
||||||
|
/* tests for ops */
|
||||||
|
|
||||||
|
/* op of size 1 */
|
||||||
|
g_assert(do_test_accounting(true,
|
||||||
|
1 * 512,
|
||||||
|
150,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1));
|
||||||
|
|
||||||
|
/* op of size 2 */
|
||||||
|
g_assert(do_test_accounting(true,
|
||||||
|
2 * 512,
|
||||||
|
150,
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1));
|
||||||
|
|
||||||
|
/* jumbo op accounting fragmentation : size 64 with op size of 13 units */
|
||||||
|
g_assert(do_test_accounting(true,
|
||||||
|
64 * 512,
|
||||||
|
150,
|
||||||
|
13 * 512,
|
||||||
|
(64.0 * 2) / 13,
|
||||||
|
(64.0 / 13),
|
||||||
|
(64.0 / 13)));
|
||||||
|
|
||||||
|
/* same with orthogonal parameters changes */
|
||||||
|
g_assert(do_test_accounting(true,
|
||||||
|
64 * 512,
|
||||||
|
300,
|
||||||
|
13 * 512,
|
||||||
|
(64.0 * 2) / 13,
|
||||||
|
(64.0 / 13),
|
||||||
|
(64.0 / 13)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
init_clocks();
|
||||||
|
do {} while (g_main_context_iteration(NULL, false));
|
||||||
|
|
||||||
|
/* tests in the same order as the header function declarations */
|
||||||
|
g_test_init(&argc, &argv, NULL);
|
||||||
|
g_test_add_func("/throttle/leak_bucket", test_leak_bucket);
|
||||||
|
g_test_add_func("/throttle/compute_wait", test_compute_wait);
|
||||||
|
g_test_add_func("/throttle/init", test_init);
|
||||||
|
g_test_add_func("/throttle/destroy", test_destroy);
|
||||||
|
g_test_add_func("/throttle/have_timer", test_have_timer);
|
||||||
|
g_test_add_func("/throttle/config/enabled", test_enabled);
|
||||||
|
g_test_add_func("/throttle/config/conflicting", test_conflicting_config);
|
||||||
|
g_test_add_func("/throttle/config/is_valid", test_is_valid);
|
||||||
|
g_test_add_func("/throttle/config_functions", test_config_functions);
|
||||||
|
g_test_add_func("/throttle/accounting", test_accounting);
|
||||||
|
return g_test_run();
|
||||||
|
}
|
||||||
|
|
@ -11,3 +11,4 @@ util-obj-y += iov.o aes.o qemu-config.o qemu-sockets.o uri.o notify.o
|
|||||||
util-obj-y += qemu-option.o qemu-progress.o
|
util-obj-y += qemu-option.o qemu-progress.o
|
||||||
util-obj-y += hexdump.o
|
util-obj-y += hexdump.o
|
||||||
util-obj-y += crc32c.o
|
util-obj-y += crc32c.o
|
||||||
|
util-obj-y += throttle.o
|
||||||
|
396
util/throttle.c
Normal file
396
util/throttle.c
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
/*
|
||||||
|
* QEMU throttling infrastructure
|
||||||
|
*
|
||||||
|
* Copyright (C) Nodalink, SARL. 2013
|
||||||
|
*
|
||||||
|
* Author:
|
||||||
|
* Benoît Canet <benoit.canet@irqsave.net>
|
||||||
|
*
|
||||||
|
* 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 or
|
||||||
|
* (at your option) version 3 of the License.
|
||||||
|
*
|
||||||
|
* 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/throttle.h"
|
||||||
|
#include "qemu/timer.h"
|
||||||
|
|
||||||
|
/* This function make a bucket leak
|
||||||
|
*
|
||||||
|
* @bkt: the bucket to make leak
|
||||||
|
* @delta_ns: the time delta
|
||||||
|
*/
|
||||||
|
void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta_ns)
|
||||||
|
{
|
||||||
|
double leak;
|
||||||
|
|
||||||
|
/* compute how much to leak */
|
||||||
|
leak = (bkt->avg * (double) delta_ns) / NANOSECONDS_PER_SECOND;
|
||||||
|
|
||||||
|
/* make the bucket leak */
|
||||||
|
bkt->level = MAX(bkt->level - leak, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the time delta since last leak and make proportionals leaks
|
||||||
|
*
|
||||||
|
* @now: the current timestamp in ns
|
||||||
|
*/
|
||||||
|
static void throttle_do_leak(ThrottleState *ts, int64_t now)
|
||||||
|
{
|
||||||
|
/* compute the time elapsed since the last leak */
|
||||||
|
int64_t delta_ns = now - ts->previous_leak;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ts->previous_leak = now;
|
||||||
|
|
||||||
|
if (delta_ns <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make each bucket leak */
|
||||||
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
||||||
|
throttle_leak_bucket(&ts->cfg.buckets[i], delta_ns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do the real job of computing the time to wait
|
||||||
|
*
|
||||||
|
* @limit: the throttling limit
|
||||||
|
* @extra: the number of operation to delay
|
||||||
|
* @ret: the time to wait in ns
|
||||||
|
*/
|
||||||
|
static int64_t throttle_do_compute_wait(double limit, double extra)
|
||||||
|
{
|
||||||
|
double wait = extra * NANOSECONDS_PER_SECOND;
|
||||||
|
wait /= limit;
|
||||||
|
return wait;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function compute the wait time in ns that a leaky bucket should trigger
|
||||||
|
*
|
||||||
|
* @bkt: the leaky bucket we operate on
|
||||||
|
* @ret: the resulting wait time in ns or 0 if the operation can go through
|
||||||
|
*/
|
||||||
|
int64_t throttle_compute_wait(LeakyBucket *bkt)
|
||||||
|
{
|
||||||
|
double extra; /* the number of extra units blocking the io */
|
||||||
|
|
||||||
|
if (!bkt->avg) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extra = bkt->level - bkt->max;
|
||||||
|
|
||||||
|
if (extra <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return throttle_do_compute_wait(bkt->avg, extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function compute the time that must be waited while this IO
|
||||||
|
*
|
||||||
|
* @is_write: true if the current IO is a write, false if it's a read
|
||||||
|
* @ret: time to wait
|
||||||
|
*/
|
||||||
|
static int64_t throttle_compute_wait_for(ThrottleState *ts,
|
||||||
|
bool is_write)
|
||||||
|
{
|
||||||
|
BucketType to_check[2][4] = { {THROTTLE_BPS_TOTAL,
|
||||||
|
THROTTLE_OPS_TOTAL,
|
||||||
|
THROTTLE_BPS_READ,
|
||||||
|
THROTTLE_OPS_READ},
|
||||||
|
{THROTTLE_BPS_TOTAL,
|
||||||
|
THROTTLE_OPS_TOTAL,
|
||||||
|
THROTTLE_BPS_WRITE,
|
||||||
|
THROTTLE_OPS_WRITE}, };
|
||||||
|
int64_t wait, max_wait = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
BucketType index = to_check[is_write][i];
|
||||||
|
wait = throttle_compute_wait(&ts->cfg.buckets[index]);
|
||||||
|
if (wait > max_wait) {
|
||||||
|
max_wait = wait;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return max_wait;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compute the timer for this type of operation
|
||||||
|
*
|
||||||
|
* @is_write: the type of operation
|
||||||
|
* @now: the current clock timestamp
|
||||||
|
* @next_timestamp: the resulting timer
|
||||||
|
* @ret: true if a timer must be set
|
||||||
|
*/
|
||||||
|
bool throttle_compute_timer(ThrottleState *ts,
|
||||||
|
bool is_write,
|
||||||
|
int64_t now,
|
||||||
|
int64_t *next_timestamp)
|
||||||
|
{
|
||||||
|
int64_t wait;
|
||||||
|
|
||||||
|
/* leak proportionally to the time elapsed */
|
||||||
|
throttle_do_leak(ts, now);
|
||||||
|
|
||||||
|
/* compute the wait time if any */
|
||||||
|
wait = throttle_compute_wait_for(ts, is_write);
|
||||||
|
|
||||||
|
/* if the code must wait compute when the next timer should fire */
|
||||||
|
if (wait) {
|
||||||
|
*next_timestamp = now + wait;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* else no need to wait at all */
|
||||||
|
*next_timestamp = now;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* To be called first on the ThrottleState */
|
||||||
|
void throttle_init(ThrottleState *ts,
|
||||||
|
QEMUClockType clock_type,
|
||||||
|
QEMUTimerCB *read_timer_cb,
|
||||||
|
QEMUTimerCB *write_timer_cb,
|
||||||
|
void *timer_opaque)
|
||||||
|
{
|
||||||
|
memset(ts, 0, sizeof(ThrottleState));
|
||||||
|
|
||||||
|
ts->clock_type = clock_type;
|
||||||
|
ts->timers[0] = timer_new_ns(clock_type, read_timer_cb, timer_opaque);
|
||||||
|
ts->timers[1] = timer_new_ns(clock_type, write_timer_cb, timer_opaque);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* destroy a timer */
|
||||||
|
static void throttle_timer_destroy(QEMUTimer **timer)
|
||||||
|
{
|
||||||
|
assert(*timer != NULL);
|
||||||
|
|
||||||
|
timer_del(*timer);
|
||||||
|
timer_free(*timer);
|
||||||
|
*timer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* To be called last on the ThrottleState */
|
||||||
|
void throttle_destroy(ThrottleState *ts)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
throttle_timer_destroy(&ts->timers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* is any throttling timer configured */
|
||||||
|
bool throttle_have_timer(ThrottleState *ts)
|
||||||
|
{
|
||||||
|
if (ts->timers[0]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Does any throttling must be done
|
||||||
|
*
|
||||||
|
* @cfg: the throttling configuration to inspect
|
||||||
|
* @ret: true if throttling must be done else false
|
||||||
|
*/
|
||||||
|
bool throttle_enabled(ThrottleConfig *cfg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
||||||
|
if (cfg->buckets[i].avg > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return true if any two throttling parameters conflicts
|
||||||
|
*
|
||||||
|
* @cfg: the throttling configuration to inspect
|
||||||
|
* @ret: true if any conflict detected else false
|
||||||
|
*/
|
||||||
|
bool throttle_conflicting(ThrottleConfig *cfg)
|
||||||
|
{
|
||||||
|
bool bps_flag, ops_flag;
|
||||||
|
bool bps_max_flag, ops_max_flag;
|
||||||
|
|
||||||
|
bps_flag = cfg->buckets[THROTTLE_BPS_TOTAL].avg &&
|
||||||
|
(cfg->buckets[THROTTLE_BPS_READ].avg ||
|
||||||
|
cfg->buckets[THROTTLE_BPS_WRITE].avg);
|
||||||
|
|
||||||
|
ops_flag = cfg->buckets[THROTTLE_OPS_TOTAL].avg &&
|
||||||
|
(cfg->buckets[THROTTLE_OPS_READ].avg ||
|
||||||
|
cfg->buckets[THROTTLE_OPS_WRITE].avg);
|
||||||
|
|
||||||
|
bps_max_flag = cfg->buckets[THROTTLE_BPS_TOTAL].max &&
|
||||||
|
(cfg->buckets[THROTTLE_BPS_READ].max ||
|
||||||
|
cfg->buckets[THROTTLE_BPS_WRITE].max);
|
||||||
|
|
||||||
|
ops_max_flag = cfg->buckets[THROTTLE_OPS_TOTAL].max &&
|
||||||
|
(cfg->buckets[THROTTLE_OPS_READ].max ||
|
||||||
|
cfg->buckets[THROTTLE_OPS_WRITE].max);
|
||||||
|
|
||||||
|
return bps_flag || ops_flag || bps_max_flag || ops_max_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if a throttling configuration is valid
|
||||||
|
* @cfg: the throttling configuration to inspect
|
||||||
|
* @ret: true if valid else false
|
||||||
|
*/
|
||||||
|
bool throttle_is_valid(ThrottleConfig *cfg)
|
||||||
|
{
|
||||||
|
bool invalid = false;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
||||||
|
if (cfg->buckets[i].avg < 0) {
|
||||||
|
invalid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
||||||
|
if (cfg->buckets[i].max < 0) {
|
||||||
|
invalid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fix bucket parameters */
|
||||||
|
static void throttle_fix_bucket(LeakyBucket *bkt)
|
||||||
|
{
|
||||||
|
double min;
|
||||||
|
|
||||||
|
/* zero bucket level */
|
||||||
|
bkt->level = 0;
|
||||||
|
|
||||||
|
/* The following is done to cope with the Linux CFQ block scheduler
|
||||||
|
* which regroup reads and writes by block of 100ms in the guest.
|
||||||
|
* When they are two process one making reads and one making writes cfq
|
||||||
|
* make a pattern looking like the following:
|
||||||
|
* WWWWWWWWWWWRRRRRRRRRRRRRRWWWWWWWWWWWWWwRRRRRRRRRRRRRRRRR
|
||||||
|
* Having a max burst value of 100ms of the average will help smooth the
|
||||||
|
* throttling
|
||||||
|
*/
|
||||||
|
min = bkt->avg / 10;
|
||||||
|
if (bkt->avg && !bkt->max) {
|
||||||
|
bkt->max = min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* take care of canceling a timer */
|
||||||
|
static void throttle_cancel_timer(QEMUTimer *timer)
|
||||||
|
{
|
||||||
|
assert(timer != NULL);
|
||||||
|
|
||||||
|
timer_del(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used to configure the throttle
|
||||||
|
*
|
||||||
|
* @ts: the throttle state we are working on
|
||||||
|
* @cfg: the config to set
|
||||||
|
*/
|
||||||
|
void throttle_config(ThrottleState *ts, ThrottleConfig *cfg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
ts->cfg = *cfg;
|
||||||
|
|
||||||
|
for (i = 0; i < BUCKETS_COUNT; i++) {
|
||||||
|
throttle_fix_bucket(&ts->cfg.buckets[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ts->previous_leak = qemu_clock_get_ns(ts->clock_type);
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
throttle_cancel_timer(ts->timers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* used to get config
|
||||||
|
*
|
||||||
|
* @ts: the throttle state we are working on
|
||||||
|
* @cfg: the config to write
|
||||||
|
*/
|
||||||
|
void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg)
|
||||||
|
{
|
||||||
|
*cfg = ts->cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Schedule the read or write timer if needed
|
||||||
|
*
|
||||||
|
* NOTE: this function is not unit tested due to it's usage of timer_mod
|
||||||
|
*
|
||||||
|
* @is_write: the type of operation (read/write)
|
||||||
|
* @ret: true if the timer has been scheduled else false
|
||||||
|
*/
|
||||||
|
bool throttle_schedule_timer(ThrottleState *ts, bool is_write)
|
||||||
|
{
|
||||||
|
int64_t now = qemu_clock_get_ns(ts->clock_type);
|
||||||
|
int64_t next_timestamp;
|
||||||
|
bool must_wait;
|
||||||
|
|
||||||
|
must_wait = throttle_compute_timer(ts,
|
||||||
|
is_write,
|
||||||
|
now,
|
||||||
|
&next_timestamp);
|
||||||
|
|
||||||
|
/* request not throttled */
|
||||||
|
if (!must_wait) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* request throttled and timer pending -> do nothing */
|
||||||
|
if (timer_pending(ts->timers[is_write])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* request throttled and timer not pending -> arm timer */
|
||||||
|
timer_mod(ts->timers[is_write], next_timestamp);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do the accounting for this operation
|
||||||
|
*
|
||||||
|
* @is_write: the type of operation (read/write)
|
||||||
|
* @size: the size of the operation
|
||||||
|
*/
|
||||||
|
void throttle_account(ThrottleState *ts, bool is_write, uint64_t size)
|
||||||
|
{
|
||||||
|
double units = 1.0;
|
||||||
|
|
||||||
|
/* if cfg.op_size is defined and smaller than size we compute unit count */
|
||||||
|
if (ts->cfg.op_size && size > ts->cfg.op_size) {
|
||||||
|
units = (double) size / ts->cfg.op_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ts->cfg.buckets[THROTTLE_BPS_TOTAL].level += size;
|
||||||
|
ts->cfg.buckets[THROTTLE_OPS_TOTAL].level += units;
|
||||||
|
|
||||||
|
if (is_write) {
|
||||||
|
ts->cfg.buckets[THROTTLE_BPS_WRITE].level += size;
|
||||||
|
ts->cfg.buckets[THROTTLE_OPS_WRITE].level += units;
|
||||||
|
} else {
|
||||||
|
ts->cfg.buckets[THROTTLE_BPS_READ].level += size;
|
||||||
|
ts->cfg.buckets[THROTTLE_OPS_READ].level += units;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user