From 3ff2f67a7c24183fcbcfe1332e5223ac6f96438c Mon Sep 17 00:00:00 2001 From: Evgeny Yakovlev Date: Mon, 18 Jul 2016 22:39:52 +0300 Subject: 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 Signed-off-by: Denis V. Lunev Reviewed-by: Paolo Bonzini Message-id: 1468870792-7411-5-git-send-email-den@openvz.org CC: Kevin Wolf CC: Max Reitz CC: Stefan Hajnoczi CC: Fam Zheng CC: John Snow Signed-off-by: John Snow --- block/io.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'block') diff --git a/block/io.c b/block/io.c index 2887394633..cfda7148d8 100644 --- a/block/io.c +++ b/block/io.c @@ -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); -- cgit v1.2.3