block: ignore flush requests when storage is clean

Some guests (win2008 server for example) do a lot of unnecessary
flushing when underlying media has not changed. This adds additional
overhead on host when calling fsync/fdatasync.

This change introduces a write generation scheme in BlockDriverState.
Current write generation is checked against last flushed generation to
avoid unnessesary flushes.

The problem with excessive flushing was found by a performance test
which does parallel directory tree creation (from 2 processes).
Results improved from 0.424 loops/sec to 0.432 loops/sec.
Each loop creates 10^3 directories with 10 files in each.

This affected some blkdebug testcases that were expecting error logs from
failure-injected flushes which are now skipped entirely
(tests 026 071 089).

This also affects the performance of block jobs and thus BLOCK_JOB_READY
events for driver-mirror and active block-commit commands now arrives
faster, before QMP send successfully returns to caller (tests 141 144).

Signed-off-by: Evgeny Yakovlev <eyakovlev@virtuozzo.com>
Signed-off-by: Denis V. Lunev <den@openvz.org>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-id: 1468870792-7411-5-git-send-email-den@openvz.org
CC: Kevin Wolf <kwolf@redhat.com>
CC: Max Reitz <mreitz@redhat.com>
CC: Stefan Hajnoczi <stefanha@redhat.com>
CC: Fam Zheng <famz@redhat.com>
CC: John Snow <jsnow@redhat.com>
Signed-off-by: John Snow <jsnow@redhat.com>
This commit is contained in:
Evgeny Yakovlev 2016-07-18 22:39:52 +03:00 committed by John Snow
parent 2dd7e10d7c
commit 3ff2f67a7c
9 changed files with 32 additions and 113 deletions

View file

@ -1303,6 +1303,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
}
bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE);
++bs->write_gen;
bdrv_set_dirty(bs, start_sector, end_sector - start_sector);
if (bs->wr_highest_offset < offset + bytes) {
@ -2236,6 +2237,15 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
tracked_request_begin(&req, bs, 0, 0, BDRV_TRACKED_FLUSH);
int current_gen = bs->write_gen;
/* Wait until any previous flushes are completed */
while (bs->flush_started_gen != bs->flushed_gen) {
qemu_co_queue_wait(&bs->flush_queue);
}
bs->flush_started_gen = current_gen;
/* Write back all layers by calling one driver function */
if (bs->drv->bdrv_co_flush) {
ret = bs->drv->bdrv_co_flush(bs);
@ -2256,6 +2266,11 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
goto flush_parent;
}
/* Check if we really need to flush anything */
if (bs->flushed_gen == current_gen) {
goto flush_parent;
}
BLKDBG_EVENT(bs->file, BLKDBG_FLUSH_TO_DISK);
if (bs->drv->bdrv_co_flush_to_disk) {
ret = bs->drv->bdrv_co_flush_to_disk(bs);
@ -2286,6 +2301,7 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
*/
ret = 0;
}
if (ret < 0) {
goto out;
}
@ -2296,6 +2312,10 @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
flush_parent:
ret = bs->file ? bdrv_co_flush(bs->file->bs) : 0;
out:
/* Notify any pending flushes that we have completed */
bs->flushed_gen = current_gen;
qemu_co_queue_restart_all(&bs->flush_queue);
tracked_request_end(&req);
return ret;
}
@ -2421,6 +2441,7 @@ int coroutine_fn bdrv_co_discard(BlockDriverState *bs, int64_t sector_num,
}
ret = 0;
out:
++bs->write_gen;
bdrv_set_dirty(bs, req.offset >> BDRV_SECTOR_BITS,
req.bytes >> BDRV_SECTOR_BITS);
tracked_request_end(&req);