qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v7 00/21] preallocate filter
@ 2020-10-21 14:58 Vladimir Sementsov-Ogievskiy
  2020-10-21 14:58 ` [PATCH v7 01/21] block: simplify comment to BDRV_REQ_SERIALISING Vladimir Sementsov-Ogievskiy
                   ` (21 more replies)
  0 siblings, 22 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Hi all!

Here is a filter, which does preallocation on write.

v7:
01: add Alberto's r-b
07: don't remove sentence from the comment
08: - drop extra "s->file_end = end;" line
    - improve check/set perm handlers
09: add Max's r-b
10: add Max's r-b
11: new
12: - skip if preallocate unsupported
    - drop auto and quick groups
13: new
14: - improve 'average' field of result spec
    - drop extra 'dim = ...' line
15-18: new
19: a lot of changes
20: new
21: add results dump to json

In Virtuozzo we have to deal with some custom distributed storage
solution, where allocation is very-very expensive operation. We have to
workaround it in Qemu, so here is a new filter.

Still, the filter shows good results for me even for xfs and ext4.

Here are results, produced by new benchmark (last several patches):

All results are in iops (larger means better)

----------------------------------  -------------  ------------
                                    no-prealloc    prealloc
ssd-ext4, aligned sequential 16k    2e+04          2.7e+04
                                                     +36%
hdd-ext4, aligned sequential 16k    4.8e+03 ± 5%   3.1e+03 ± 4%
                                                     -36%
ssd-xfs, aligned sequential 16k     1.6e+04 ± 5%   2.5e+04 ± 3%
                                                     +59%
hdd-xfs, aligned sequential 16k     4.7e+03 ± 22%  3.2e+03 ± 3%
                                                     -31%
ssd-ext4, unaligned sequential 64k  2e+04          2.7e+04
                                                     +36%
hdd-ext4, unaligned sequential 64k  4.9e+03 ± 2%   3.2e+03 ± 8%
                                                     -34%
ssd-xfs, unaligned sequential 64k   1.5e+04        2.6e+04 ± 2%
                                                     +69%
hdd-xfs, unaligned sequential 64k   5.2e+03 ± 2%   3.3e+03 ± 5%
                                                     -35%
----------------------------------  -------------  ------------

Note: it's on Fedora 30, kernel 5.6.13-100.fc30.x86_64

The tests are actually qemu-img bench, run like:

  ./qemu-img create -f qcow2 $img 16G

aligned:
  ./qemu-img bench -c 10000 -d 64 -f qcow2  -s 16k -t none -n -w $img

unaligned
  ./qemu-img bench -c 10000 -d 64 -f qcow2 -o 1k -s 64k -t none -n -w $img

and for preallocation, you'll drop -f qcow2, add --image-opts, and
instead of just $img use
  driver=qcow2,file.driver=preallocate,file.file.driver=file,file.file.filename=$img 

Or, use new benchmark like this:

  ./bench_prealloc.py ../../build/qemu-img \
      ssd-ext4:/path/to/mount/point \
      ssd-xfs:/path2 hdd-ext4:/path3 hdd-xfs:/path4

Vladimir Sementsov-Ogievskiy (21):
  block: simplify comment to BDRV_REQ_SERIALISING
  block/io.c: drop assertion on double waiting for request serialisation
  block/io: split out bdrv_find_conflicting_request
  block/io: bdrv_wait_serialising_requests_locked: drop extra bs arg
  block: bdrv_mark_request_serialising: split non-waiting function
  block: introduce BDRV_REQ_NO_WAIT flag
  block: bdrv_check_perm(): process children anyway
  block: introduce preallocate filter
  qemu-io: add preallocate mode parameter for truncate command
  iotests: qemu_io_silent: support --image-opts
  iotests.py: execute_setup_common(): add required_fmts argument
  iotests: add 298 to test new preallocate filter driver
  scripts/simplebench: fix grammar: s/successed/succeeded/
  scripts/simplebench: support iops
  scripts/simplebench: use standard deviation for +- error
  simplebench: rename ascii() to results_to_text()
  simplebench: move results_to_text() into separate file
  simplebench/results_to_text: improve view of the table
  simplebench/results_to_text: add difference line to the table
  simplebench/results_to_text: make executable
  scripts/simplebench: add bench_prealloc.py

 docs/system/qemu-block-drivers.rst.inc |  26 ++
 qapi/block-core.json                   |  20 +-
 include/block/block.h                  |  20 +-
 include/block/block_int.h              |   3 +-
 block.c                                |   7 +-
 block/file-posix.c                     |   2 +-
 block/io.c                             | 130 +++---
 block/preallocate.c                    | 559 +++++++++++++++++++++++++
 qemu-io-cmds.c                         |  46 +-
 block/meson.build                      |   1 +
 scripts/simplebench/bench-example.py   |   3 +-
 scripts/simplebench/bench_prealloc.py  | 132 ++++++
 scripts/simplebench/bench_write_req.py |   3 +-
 scripts/simplebench/results_to_text.py | 126 ++++++
 scripts/simplebench/simplebench.py     |  66 ++-
 tests/qemu-iotests/298                 | 186 ++++++++
 tests/qemu-iotests/298.out             |   5 +
 tests/qemu-iotests/group               |   1 +
 tests/qemu-iotests/iotests.py          |  16 +-
 19 files changed, 1225 insertions(+), 127 deletions(-)
 create mode 100644 block/preallocate.c
 create mode 100755 scripts/simplebench/bench_prealloc.py
 create mode 100755 scripts/simplebench/results_to_text.py
 create mode 100644 tests/qemu-iotests/298
 create mode 100644 tests/qemu-iotests/298.out

-- 
2.21.3



^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v7 01/21] block: simplify comment to BDRV_REQ_SERIALISING
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-10-21 14:58 ` [PATCH v7 02/21] block/io.c: drop assertion on double waiting for request serialisation Vladimir Sementsov-Ogievskiy
                   ` (20 subsequent siblings)
  21 siblings, 0 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den, Alberto Garcia

1. BDRV_REQ_NO_SERIALISING doesn't exist already, don't mention it.

2. We are going to add one more user of BDRV_REQ_SERIALISING, so
   comment about backup becomes a bit confusing here. The use case in
   backup is documented in block/backup.c, so let's just drop
   duplication here.

3. The fact that BDRV_REQ_SERIALISING is only for write requests is
   omitted. Add a note.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Alberto Garcia <berto@igalia.com>
---
 include/block/block.h | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/include/block/block.h b/include/block/block.h
index d16c401cb4..f9ca42fd4b 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -63,16 +63,7 @@ typedef enum {
      * content. */
     BDRV_REQ_WRITE_UNCHANGED    = 0x40,
 
-    /*
-     * BDRV_REQ_SERIALISING forces request serialisation for writes.
-     * It is used to ensure that writes to the backing file of a backup process
-     * target cannot race with a read of the backup target that defers to the
-     * backing file.
-     *
-     * Note, that BDRV_REQ_SERIALISING is _not_ opposite in meaning to
-     * BDRV_REQ_NO_SERIALISING. A more descriptive name for the latter might be
-     * _DO_NOT_WAIT_FOR_SERIALISING, except that is too long.
-     */
+    /* Forces request serialisation. Use only with write requests. */
     BDRV_REQ_SERIALISING        = 0x80,
 
     /* Execute the request only if the operation can be offloaded or otherwise
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 02/21] block/io.c: drop assertion on double waiting for request serialisation
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
  2020-10-21 14:58 ` [PATCH v7 01/21] block: simplify comment to BDRV_REQ_SERIALISING Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-10-21 14:58 ` [PATCH v7 03/21] block/io: split out bdrv_find_conflicting_request Vladimir Sementsov-Ogievskiy
                   ` (19 subsequent siblings)
  21 siblings, 0 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den, Paolo Bonzini

The comments states, that on misaligned request we should have already
been waiting. But for bdrv_padding_rmw_read, we called
bdrv_mark_request_serialising with align = request_alignment, and now
we serialise with align = cluster_size. So we may have to wait again
with larger alignment.

Note, that the only user of BDRV_REQ_SERIALISING is backup which issues
cluster-aligned requests, so seems the assertion should not fire for
now. But it's wrong anyway.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
---
 block/io.c | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/block/io.c b/block/io.c
index 54f0968aee..bf6d4d5e77 100644
--- a/block/io.c
+++ b/block/io.c
@@ -1784,7 +1784,6 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes,
                           BdrvTrackedRequest *req, int flags)
 {
     BlockDriverState *bs = child->bs;
-    bool waited;
     int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE);
 
     if (bs->read_only) {
@@ -1796,15 +1795,7 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes,
     assert(!(flags & ~BDRV_REQ_MASK));
 
     if (flags & BDRV_REQ_SERIALISING) {
-        waited = bdrv_mark_request_serialising(req, bdrv_get_cluster_size(bs));
-        /*
-         * For a misaligned request we should have already waited earlier,
-         * because we come after bdrv_padding_rmw_read which must be called
-         * with the request already marked as serialising.
-         */
-        assert(!waited ||
-               (req->offset == req->overlap_offset &&
-                req->bytes == req->overlap_bytes));
+        bdrv_mark_request_serialising(req, bdrv_get_cluster_size(bs));
     } else {
         bdrv_wait_serialising_requests(req);
     }
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 03/21] block/io: split out bdrv_find_conflicting_request
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
  2020-10-21 14:58 ` [PATCH v7 01/21] block: simplify comment to BDRV_REQ_SERIALISING Vladimir Sementsov-Ogievskiy
  2020-10-21 14:58 ` [PATCH v7 02/21] block/io.c: drop assertion on double waiting for request serialisation Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-10-21 14:58 ` [PATCH v7 04/21] block/io: bdrv_wait_serialising_requests_locked: drop extra bs arg Vladimir Sementsov-Ogievskiy
                   ` (18 subsequent siblings)
  21 siblings, 0 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

To be reused in separate.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 block/io.c | 71 +++++++++++++++++++++++++++++++-----------------------
 1 file changed, 41 insertions(+), 30 deletions(-)

diff --git a/block/io.c b/block/io.c
index bf6d4d5e77..c2d9b1f849 100644
--- a/block/io.c
+++ b/block/io.c
@@ -728,43 +728,54 @@ static bool tracked_request_overlaps(BdrvTrackedRequest *req,
     return true;
 }
 
+/* Called with self->bs->reqs_lock held */
+static BdrvTrackedRequest *
+bdrv_find_conflicting_request(BdrvTrackedRequest *self)
+{
+    BdrvTrackedRequest *req;
+
+    QLIST_FOREACH(req, &self->bs->tracked_requests, list) {
+        if (req == self || (!req->serialising && !self->serialising)) {
+            continue;
+        }
+        if (tracked_request_overlaps(req, self->overlap_offset,
+                                     self->overlap_bytes))
+        {
+            /*
+             * Hitting this means there was a reentrant request, for
+             * example, a block driver issuing nested requests.  This must
+             * never happen since it means deadlock.
+             */
+            assert(qemu_coroutine_self() != req->co);
+
+            /*
+             * If the request is already (indirectly) waiting for us, or
+             * will wait for us as soon as it wakes up, then just go on
+             * (instead of producing a deadlock in the former case).
+             */
+            if (!req->waiting_for) {
+                return req;
+            }
+        }
+    }
+
+    return NULL;
+}
+
 static bool coroutine_fn
 bdrv_wait_serialising_requests_locked(BlockDriverState *bs,
                                       BdrvTrackedRequest *self)
 {
     BdrvTrackedRequest *req;
-    bool retry;
     bool waited = false;
 
-    do {
-        retry = false;
-        QLIST_FOREACH(req, &bs->tracked_requests, list) {
-            if (req == self || (!req->serialising && !self->serialising)) {
-                continue;
-            }
-            if (tracked_request_overlaps(req, self->overlap_offset,
-                                         self->overlap_bytes))
-            {
-                /* Hitting this means there was a reentrant request, for
-                 * example, a block driver issuing nested requests.  This must
-                 * never happen since it means deadlock.
-                 */
-                assert(qemu_coroutine_self() != req->co);
-
-                /* If the request is already (indirectly) waiting for us, or
-                 * will wait for us as soon as it wakes up, then just go on
-                 * (instead of producing a deadlock in the former case). */
-                if (!req->waiting_for) {
-                    self->waiting_for = req;
-                    qemu_co_queue_wait(&req->wait_queue, &bs->reqs_lock);
-                    self->waiting_for = NULL;
-                    retry = true;
-                    waited = true;
-                    break;
-                }
-            }
-        }
-    } while (retry);
+    while ((req = bdrv_find_conflicting_request(self))) {
+        self->waiting_for = req;
+        qemu_co_queue_wait(&req->wait_queue, &bs->reqs_lock);
+        self->waiting_for = NULL;
+        waited = true;
+    }
+
     return waited;
 }
 
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 04/21] block/io: bdrv_wait_serialising_requests_locked: drop extra bs arg
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (2 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 03/21] block/io: split out bdrv_find_conflicting_request Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-10-21 14:58 ` [PATCH v7 05/21] block: bdrv_mark_request_serialising: split non-waiting function Vladimir Sementsov-Ogievskiy
                   ` (17 subsequent siblings)
  21 siblings, 0 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

bs is linked in req, so no needs to pass it separately. Most of
tracked-requests API doesn't have bs argument. Actually, after this
patch only tracked_request_begin has it, but it's for purpose.

While being here, also add a comment about what "_locked" is.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 block/io.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/block/io.c b/block/io.c
index c2d9b1f849..5c1a1b388b 100644
--- a/block/io.c
+++ b/block/io.c
@@ -762,16 +762,16 @@ bdrv_find_conflicting_request(BdrvTrackedRequest *self)
     return NULL;
 }
 
+/* Called with self->bs->reqs_lock held */
 static bool coroutine_fn
-bdrv_wait_serialising_requests_locked(BlockDriverState *bs,
-                                      BdrvTrackedRequest *self)
+bdrv_wait_serialising_requests_locked(BdrvTrackedRequest *self)
 {
     BdrvTrackedRequest *req;
     bool waited = false;
 
     while ((req = bdrv_find_conflicting_request(self))) {
         self->waiting_for = req;
-        qemu_co_queue_wait(&req->wait_queue, &bs->reqs_lock);
+        qemu_co_queue_wait(&req->wait_queue, &self->bs->reqs_lock);
         self->waiting_for = NULL;
         waited = true;
     }
@@ -795,7 +795,7 @@ bool bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
 
     req->overlap_offset = MIN(req->overlap_offset, overlap_offset);
     req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes);
-    waited = bdrv_wait_serialising_requests_locked(bs, req);
+    waited = bdrv_wait_serialising_requests_locked(req);
     qemu_co_mutex_unlock(&bs->reqs_lock);
     return waited;
 }
@@ -877,7 +877,7 @@ static bool coroutine_fn bdrv_wait_serialising_requests(BdrvTrackedRequest *self
     }
 
     qemu_co_mutex_lock(&bs->reqs_lock);
-    waited = bdrv_wait_serialising_requests_locked(bs, self);
+    waited = bdrv_wait_serialising_requests_locked(self);
     qemu_co_mutex_unlock(&bs->reqs_lock);
 
     return waited;
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 05/21] block: bdrv_mark_request_serialising: split non-waiting function
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (3 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 04/21] block/io: bdrv_wait_serialising_requests_locked: drop extra bs arg Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-10-21 14:58 ` [PATCH v7 06/21] block: introduce BDRV_REQ_NO_WAIT flag Vladimir Sementsov-Ogievskiy
                   ` (16 subsequent siblings)
  21 siblings, 0 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

We'll need a separate function, which will only "mark" request
serialising with specified align but not wait for conflicting
requests. So, it will be like old bdrv_mark_request_serialising(),
before merging bdrv_wait_serialising_requests_locked() into it.

To reduce the possible mess, let's do the following:

Public function that does both marking and waiting will be called
bdrv_make_request_serialising, and private function which will only
"mark" will be called tracked_request_set_serialising().

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
---
 include/block/block_int.h |  3 ++-
 block/file-posix.c        |  2 +-
 block/io.c                | 35 +++++++++++++++++++++++------------
 3 files changed, 26 insertions(+), 14 deletions(-)

diff --git a/include/block/block_int.h b/include/block/block_int.h
index 38cad9d15c..887b0668d8 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -1052,7 +1052,8 @@ extern unsigned int bdrv_drain_all_count;
 void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent);
 void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent);
 
-bool coroutine_fn bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align);
+bool coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req,
+                                                uint64_t align);
 BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs);
 
 int get_tmp_filename(char *filename, int size);
diff --git a/block/file-posix.c b/block/file-posix.c
index c63926d592..37d9266f6a 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -2953,7 +2953,7 @@ raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes,
         req->bytes = end - req->offset;
         req->overlap_bytes = req->bytes;
 
-        bdrv_mark_request_serialising(req, bs->bl.request_alignment);
+        bdrv_make_request_serialising(req, bs->bl.request_alignment);
     }
 #endif
 
diff --git a/block/io.c b/block/io.c
index 5c1a1b388b..24ff8d804b 100644
--- a/block/io.c
+++ b/block/io.c
@@ -779,15 +779,14 @@ bdrv_wait_serialising_requests_locked(BdrvTrackedRequest *self)
     return waited;
 }
 
-bool bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
+/* Called with req->bs->reqs_lock held */
+static void tracked_request_set_serialising(BdrvTrackedRequest *req,
+                                            uint64_t align)
 {
-    BlockDriverState *bs = req->bs;
     int64_t overlap_offset = req->offset & ~(align - 1);
     uint64_t overlap_bytes = ROUND_UP(req->offset + req->bytes, align)
                                - overlap_offset;
-    bool waited;
 
-    qemu_co_mutex_lock(&bs->reqs_lock);
     if (!req->serialising) {
         qatomic_inc(&req->bs->serialising_in_flight);
         req->serialising = true;
@@ -795,9 +794,6 @@ bool bdrv_mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
 
     req->overlap_offset = MIN(req->overlap_offset, overlap_offset);
     req->overlap_bytes = MAX(req->overlap_bytes, overlap_bytes);
-    waited = bdrv_wait_serialising_requests_locked(req);
-    qemu_co_mutex_unlock(&bs->reqs_lock);
-    return waited;
 }
 
 /**
@@ -883,6 +879,21 @@ static bool coroutine_fn bdrv_wait_serialising_requests(BdrvTrackedRequest *self
     return waited;
 }
 
+bool coroutine_fn bdrv_make_request_serialising(BdrvTrackedRequest *req,
+                                                uint64_t align)
+{
+    bool waited;
+
+    qemu_co_mutex_lock(&req->bs->reqs_lock);
+
+    tracked_request_set_serialising(req, align);
+    waited = bdrv_wait_serialising_requests_locked(req);
+
+    qemu_co_mutex_unlock(&req->bs->reqs_lock);
+
+    return waited;
+}
+
 static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
                                    size_t size)
 {
@@ -1395,7 +1406,7 @@ static int coroutine_fn bdrv_aligned_preadv(BdrvChild *child,
          * with each other for the same cluster.  For example, in copy-on-read
          * it ensures that the CoR read and write operations are atomic and
          * guest writes cannot interleave between them. */
-        bdrv_mark_request_serialising(req, bdrv_get_cluster_size(bs));
+        bdrv_make_request_serialising(req, bdrv_get_cluster_size(bs));
     } else {
         bdrv_wait_serialising_requests(req);
     }
@@ -1806,7 +1817,7 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes,
     assert(!(flags & ~BDRV_REQ_MASK));
 
     if (flags & BDRV_REQ_SERIALISING) {
-        bdrv_mark_request_serialising(req, bdrv_get_cluster_size(bs));
+        bdrv_make_request_serialising(req, bdrv_get_cluster_size(bs));
     } else {
         bdrv_wait_serialising_requests(req);
     }
@@ -1972,7 +1983,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child,
 
     padding = bdrv_init_padding(bs, offset, bytes, &pad);
     if (padding) {
-        bdrv_mark_request_serialising(req, align);
+        bdrv_make_request_serialising(req, align);
 
         bdrv_padding_rmw_read(child, req, &pad, true);
 
@@ -2086,7 +2097,7 @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child,
     }
 
     if (bdrv_pad_request(bs, &qiov, &qiov_offset, &offset, &bytes, &pad)) {
-        bdrv_mark_request_serialising(&req, align);
+        bdrv_make_request_serialising(&req, align);
         bdrv_padding_rmw_read(child, &req, &pad, false);
     }
 
@@ -3139,7 +3150,7 @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
      * new area, we need to make sure that no write requests are made to it
      * concurrently or they might be overwritten by preallocation. */
     if (new_bytes) {
-        bdrv_mark_request_serialising(&req, 1);
+        bdrv_make_request_serialising(&req, 1);
     }
     if (bs->read_only) {
         error_setg(errp, "Image is read-only");
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 06/21] block: introduce BDRV_REQ_NO_WAIT flag
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (4 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 05/21] block: bdrv_mark_request_serialising: split non-waiting function Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-10-21 14:58 ` [PATCH v7 07/21] block: bdrv_check_perm(): process children anyway Vladimir Sementsov-Ogievskiy
                   ` (15 subsequent siblings)
  21 siblings, 0 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Add flag to make serialising request no wait: if there are conflicting
requests, just return error immediately. It's will be used in upcoming
preallocate filter.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
---
 include/block/block.h |  9 ++++++++-
 block/io.c            | 11 ++++++++++-
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/include/block/block.h b/include/block/block.h
index f9ca42fd4b..8a37f9fb01 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -77,8 +77,15 @@ typedef enum {
      * written to qiov parameter which may be NULL.
      */
     BDRV_REQ_PREFETCH  = 0x200,
+
+    /*
+     * If we need to wait for other requests, just fail immediately. Used
+     * only together with BDRV_REQ_SERIALISING.
+     */
+    BDRV_REQ_NO_WAIT = 0x400,
+
     /* Mask of valid flags */
-    BDRV_REQ_MASK               = 0x3ff,
+    BDRV_REQ_MASK               = 0x7ff,
 } BdrvRequestFlags;
 
 typedef struct BlockSizes {
diff --git a/block/io.c b/block/io.c
index 24ff8d804b..504b656f87 100644
--- a/block/io.c
+++ b/block/io.c
@@ -1815,9 +1815,18 @@ bdrv_co_write_req_prepare(BdrvChild *child, int64_t offset, uint64_t bytes,
     assert(!(bs->open_flags & BDRV_O_INACTIVE));
     assert((bs->open_flags & BDRV_O_NO_IO) == 0);
     assert(!(flags & ~BDRV_REQ_MASK));
+    assert(!((flags & BDRV_REQ_NO_WAIT) && !(flags & BDRV_REQ_SERIALISING)));
 
     if (flags & BDRV_REQ_SERIALISING) {
-        bdrv_make_request_serialising(req, bdrv_get_cluster_size(bs));
+        QEMU_LOCK_GUARD(&bs->reqs_lock);
+
+        tracked_request_set_serialising(req, bdrv_get_cluster_size(bs));
+
+        if ((flags & BDRV_REQ_NO_WAIT) && bdrv_find_conflicting_request(req)) {
+            return -EBUSY;
+        }
+
+        bdrv_wait_serialising_requests_locked(req);
     } else {
         bdrv_wait_serialising_requests(req);
     }
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 07/21] block: bdrv_check_perm(): process children anyway
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (5 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 06/21] block: introduce BDRV_REQ_NO_WAIT flag Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-11-13 13:59   ` Max Reitz
  2020-10-21 14:58 ` [PATCH v7 08/21] block: introduce preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (14 subsequent siblings)
  21 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Do generic processing even for drivers which define .bdrv_check_perm
handler. It's needed for further preallocate filter: it will need to do
additional action on bdrv_check_perm, but don't want to reimplement
generic logic.

The patch doesn't change existing behaviour: the only driver that
implements bdrv_check_perm is file-posix, but it never has any
children.

Also, bdrv_set_perm() don't stop processing if driver has
.bdrv_set_perm handler as well.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 block.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/block.c b/block.c
index 430edf79bb..4f7c27aa6d 100644
--- a/block.c
+++ b/block.c
@@ -2048,8 +2048,11 @@ static int bdrv_check_perm(BlockDriverState *bs, BlockReopenQueue *q,
     }
 
     if (drv->bdrv_check_perm) {
-        return drv->bdrv_check_perm(bs, cumulative_perms,
-                                    cumulative_shared_perms, errp);
+        ret = drv->bdrv_check_perm(bs, cumulative_perms,
+                                   cumulative_shared_perms, errp);
+        if (ret < 0) {
+            return ret;
+        }
     }
 
     /* Drivers that never have children can omit .bdrv_child_perm() */
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 08/21] block: introduce preallocate filter
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (6 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 07/21] block: bdrv_check_perm(): process children anyway Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-11-13 14:32   ` Max Reitz
  2020-10-21 14:58 ` [PATCH v7 09/21] qemu-io: add preallocate mode parameter for truncate command Vladimir Sementsov-Ogievskiy
                   ` (13 subsequent siblings)
  21 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

It's intended to be inserted between format and protocol nodes to
preallocate additional space (expanding protocol file) on writes
crossing EOF. It improves performance for file-systems with slow
allocation.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 docs/system/qemu-block-drivers.rst.inc |  26 ++
 qapi/block-core.json                   |  20 +-
 block/preallocate.c                    | 559 +++++++++++++++++++++++++
 block/meson.build                      |   1 +
 4 files changed, 605 insertions(+), 1 deletion(-)
 create mode 100644 block/preallocate.c

diff --git a/docs/system/qemu-block-drivers.rst.inc b/docs/system/qemu-block-drivers.rst.inc
index b052a6d14e..60a064b232 100644
--- a/docs/system/qemu-block-drivers.rst.inc
+++ b/docs/system/qemu-block-drivers.rst.inc
@@ -952,3 +952,29 @@ on host and see if there are locks held by the QEMU process on the image file.
 More than one byte could be locked by the QEMU instance, each byte of which
 reflects a particular permission that is acquired or protected by the running
 block driver.
+
+Filter drivers
+~~~~~~~~~~~~~~
+
+QEMU supports several filter drivers, which don't store any data, but perform
+some additional tasks, hooking io requests.
+
+.. program:: filter-drivers
+.. option:: preallocate
+
+  The preallocate filter driver is intended to be inserted between format
+  and protocol nodes and preallocates some additional space
+  (expanding the protocol file) when writing past the file’s end. This can be
+  useful for file-systems with slow allocation.
+
+  Supported options:
+
+  .. program:: preallocate
+  .. option:: prealloc-align
+
+    On preallocation, align the file length to this value (in bytes), default 1M.
+
+  .. program:: preallocate
+  .. option:: prealloc-size
+
+    How much to preallocate (in bytes), default 128M.
diff --git a/qapi/block-core.json b/qapi/block-core.json
index ee5ebef7f2..e74669159c 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -2828,7 +2828,7 @@
             'cloop', 'compress', 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps',
             'gluster', 'host_cdrom', 'host_device', 'http', 'https', 'iscsi',
             'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels',
-            'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
+            'preallocate', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
             { 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
             'sheepdog',
             'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
@@ -3097,6 +3097,23 @@
   'data': { 'aes': 'QCryptoBlockOptionsQCow',
             'luks': 'QCryptoBlockOptionsLUKS'} }
 
+##
+# @BlockdevOptionsPreallocate:
+#
+# Filter driver intended to be inserted between format and protocol node
+# and do preallocation in protocol node on write.
+#
+# @prealloc-align: on preallocation, align file length to this number,
+#                  default 1048576 (1M)
+#
+# @prealloc-size: how much to preallocate, default 134217728 (128M)
+#
+# Since: 5.2
+##
+{ 'struct': 'BlockdevOptionsPreallocate',
+  'base': 'BlockdevOptionsGenericFormat',
+  'data': { '*prealloc-align': 'int', '*prealloc-size': 'int' } }
+
 ##
 # @BlockdevOptionsQcow2:
 #
@@ -4002,6 +4019,7 @@
       'null-co':    'BlockdevOptionsNull',
       'nvme':       'BlockdevOptionsNVMe',
       'parallels':  'BlockdevOptionsGenericFormat',
+      'preallocate':'BlockdevOptionsPreallocate',
       'qcow2':      'BlockdevOptionsQcow2',
       'qcow':       'BlockdevOptionsQcow',
       'qed':        'BlockdevOptionsGenericCOWFormat',
diff --git a/block/preallocate.c b/block/preallocate.c
new file mode 100644
index 0000000000..bada72e1da
--- /dev/null
+++ b/block/preallocate.c
@@ -0,0 +1,559 @@
+/*
+ * preallocate filter driver
+ *
+ * The driver performs preallocate operation: it is injected above
+ * some node, and before each write over EOF it does additional preallocating
+ * write-zeroes request.
+ *
+ * Copyright (c) 2020 Virtuozzo International GmbH.
+ *
+ * Author:
+ *  Sementsov-Ogievskiy Vladimir <vsementsov@virtuozzo.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+
+#include "qapi/error.h"
+#include "qemu/module.h"
+#include "qemu/option.h"
+#include "qemu/units.h"
+#include "block/block_int.h"
+
+
+typedef struct PreallocateOpts {
+    int64_t prealloc_size;
+    int64_t prealloc_align;
+} PreallocateOpts;
+
+typedef struct BDRVPreallocateState {
+    PreallocateOpts opts;
+
+    /*
+     * Track real data end, to crop preallocation on close. If < 0 the status is
+     * unknown.
+     *
+     * @data_end is a maximum of file size on open (or when we get write/resize
+     * permissions) and all write request ends after it. So it's safe to
+     * truncate to data_end if it is valid.
+     */
+    int64_t data_end;
+
+    /*
+     * Start of trailing preallocated area which reads as zero. May be smaller
+     * than data_end, if user does over-EOF write zero operation. If < 0 the
+     * status is unknown.
+     *
+     * If both @zero_start and @file_end are valid, the region
+     * [@zero_start, @file_end) is known to be preallocated zeroes. If @file_end
+     * is not valid, @zero_start doesn't make much sense.
+     */
+    int64_t zero_start;
+
+    /*
+     * Real end of file. Actually the cache for bdrv_getlength(bs->file->bs),
+     * to avoid extra lseek() calls on each write operation. If < 0 the status
+     * is unknown.
+     */
+    int64_t file_end;
+
+    /*
+     * All three states @data_end, @zero_start and @file_end are guaranteed to
+     * be invalid (< 0) when we don't have both exclusive BLK_PERM_RESIZE and
+     * BLK_PERM_WRITE permissions on file child.
+     */
+} BDRVPreallocateState;
+
+#define PREALLOCATE_OPT_PREALLOC_ALIGN "prealloc-align"
+#define PREALLOCATE_OPT_PREALLOC_SIZE "prealloc-size"
+static QemuOptsList runtime_opts = {
+    .name = "preallocate",
+    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
+    .desc = {
+        {
+            .name = PREALLOCATE_OPT_PREALLOC_ALIGN,
+            .type = QEMU_OPT_SIZE,
+            .help = "on preallocation, align file length to this number, "
+                "default 1M",
+        },
+        {
+            .name = PREALLOCATE_OPT_PREALLOC_SIZE,
+            .type = QEMU_OPT_SIZE,
+            .help = "how much to preallocate, default 128M",
+        },
+        { /* end of list */ }
+    },
+};
+
+static bool preallocate_absorb_opts(PreallocateOpts *dest, QDict *options,
+                                    BlockDriverState *child_bs, Error **errp)
+{
+    QemuOpts *opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
+
+    if (!qemu_opts_absorb_qdict(opts, options, errp)) {
+        return false;
+    }
+
+    dest->prealloc_align =
+        qemu_opt_get_size(opts, PREALLOCATE_OPT_PREALLOC_ALIGN, 1 * MiB);
+    dest->prealloc_size =
+        qemu_opt_get_size(opts, PREALLOCATE_OPT_PREALLOC_SIZE, 128 * MiB);
+
+    qemu_opts_del(opts);
+
+    if (!QEMU_IS_ALIGNED(dest->prealloc_align, BDRV_SECTOR_SIZE)) {
+        error_setg(errp, "prealloc-align parameter of preallocate filter "
+                   "is not aligned to %llu", BDRV_SECTOR_SIZE);
+        return false;
+    }
+
+    if (!QEMU_IS_ALIGNED(dest->prealloc_align,
+                         child_bs->bl.request_alignment)) {
+        error_setg(errp, "prealloc-align parameter of preallocate filter "
+                   "is not aligned to underlying node request alignment "
+                   "(%" PRIi32 ")", child_bs->bl.request_alignment);
+        return false;
+    }
+
+    return true;
+}
+
+static int preallocate_open(BlockDriverState *bs, QDict *options, int flags,
+                            Error **errp)
+{
+    BDRVPreallocateState *s = bs->opaque;
+
+    /*
+     * s->data_end and friends should be initialized on permission update.
+     * For this to work, mark them invalid.
+     */
+    s->file_end = s->zero_start = s->data_end = -EINVAL;
+
+    bs->file = bdrv_open_child(NULL, options, "file", bs, &child_of_bds,
+                               BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY,
+                               false, errp);
+    if (!bs->file) {
+        return -EINVAL;
+    }
+
+    if (!preallocate_absorb_opts(&s->opts, options, bs->file->bs, errp)) {
+        return -EINVAL;
+    }
+
+    bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED |
+        (BDRV_REQ_FUA & bs->file->bs->supported_write_flags);
+
+    bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED |
+        ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) &
+            bs->file->bs->supported_zero_flags);
+
+    return 0;
+}
+
+static void preallocate_close(BlockDriverState *bs)
+{
+    int ret;
+    BDRVPreallocateState *s = bs->opaque;
+
+    if (s->data_end < 0) {
+        return;
+    }
+
+    if (s->file_end < 0) {
+        s->file_end = bdrv_getlength(bs->file->bs);
+        if (s->file_end < 0) {
+            return;
+        }
+    }
+
+    if (s->data_end < s->file_end) {
+        ret = bdrv_truncate(bs->file, s->data_end, true, PREALLOC_MODE_OFF, 0,
+                            NULL);
+        s->file_end = ret < 0 ? ret : s->data_end;
+    }
+}
+
+
+/*
+ * Handle reopen.
+ *
+ * We must implement reopen handlers, otherwise reopen just don't work. Handle
+ * new options and don't care about preallocation state, as it is handled in
+ * set/check permission handlers.
+ */
+
+static int preallocate_reopen_prepare(BDRVReopenState *reopen_state,
+                                      BlockReopenQueue *queue, Error **errp)
+{
+    PreallocateOpts *opts = g_new0(PreallocateOpts, 1);
+
+    if (!preallocate_absorb_opts(opts, reopen_state->options,
+                                 reopen_state->bs->file->bs, errp)) {
+        g_free(opts);
+        return -EINVAL;
+    }
+
+    reopen_state->opaque = opts;
+
+    return 0;
+}
+
+static void preallocate_reopen_commit(BDRVReopenState *state)
+{
+    BDRVPreallocateState *s = state->bs->opaque;
+
+    s->opts = *(PreallocateOpts *)state->opaque;
+
+    g_free(state->opaque);
+    state->opaque = NULL;
+}
+
+static void preallocate_reopen_abort(BDRVReopenState *state)
+{
+    g_free(state->opaque);
+    state->opaque = NULL;
+}
+
+static coroutine_fn int preallocate_co_preadv_part(
+        BlockDriverState *bs, uint64_t offset, uint64_t bytes,
+        QEMUIOVector *qiov, size_t qiov_offset, int flags)
+{
+    return bdrv_co_preadv_part(bs->file, offset, bytes, qiov, qiov_offset,
+                               flags);
+}
+
+static int coroutine_fn preallocate_co_pdiscard(BlockDriverState *bs,
+                                               int64_t offset, int bytes)
+{
+    return bdrv_co_pdiscard(bs->file, offset, bytes);
+}
+
+static bool can_write_resize(uint64_t perm)
+{
+    return (perm & BLK_PERM_WRITE) && (perm & BLK_PERM_RESIZE);
+}
+
+static bool has_prealloc_perms(BlockDriverState *bs)
+{
+    BDRVPreallocateState *s = bs->opaque;
+
+    if (can_write_resize(bs->file->perm)) {
+        assert(!(bs->file->shared_perm & BLK_PERM_WRITE));
+        assert(!(bs->file->shared_perm & BLK_PERM_RESIZE));
+        return true;
+    }
+
+    assert(s->data_end < 0);
+    assert(s->zero_start < 0);
+    assert(s->file_end < 0);
+    return false;
+}
+
+/*
+ * Call on each write. Returns true if @want_merge_zero is true and the region
+ * [offset, offset + bytes) is zeroed (as a result of this call or earlier
+ * preallocation).
+ *
+ * want_merge_zero is used to merge write-zero request with preallocation in
+ * one bdrv_co_pwrite_zeroes() call.
+ */
+static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset,
+                                      int64_t bytes, bool want_merge_zero)
+{
+    BDRVPreallocateState *s = bs->opaque;
+    int64_t end = offset + bytes;
+    int64_t prealloc_start, prealloc_end;
+    int ret;
+
+    if (!has_prealloc_perms(bs)) {
+        /* We don't have state neither should try to recover it */
+        return false;
+    }
+
+    if (s->data_end < 0) {
+        s->data_end = bdrv_getlength(bs->file->bs);
+        if (s->data_end < 0) {
+            return false;
+        }
+
+        if (s->file_end < 0) {
+            s->file_end = s->data_end;
+        }
+    }
+
+    if (end <= s->data_end) {
+        return false;
+    }
+
+    /* We have valid s->data_end, and request writes beyond it. */
+
+    s->data_end = end;
+    if (s->zero_start < 0 || !want_merge_zero) {
+        s->zero_start = end;
+    }
+
+    if (s->file_end < 0) {
+        s->file_end = bdrv_getlength(bs->file->bs);
+        if (s->file_end < 0) {
+            return false;
+        }
+    }
+
+    /* Now s->data_end, s->zero_start and s->file_end are valid. */
+
+    if (end <= s->file_end) {
+        /* No preallocation needed. */
+        return want_merge_zero && offset >= s->zero_start;
+    }
+
+    /* Now we want new preallocation, as request writes beyond s->data_end. */
+
+    prealloc_start = want_merge_zero ? MIN(offset, s->file_end) : s->file_end;
+    prealloc_end = QEMU_ALIGN_UP(end + s->opts.prealloc_size,
+                                 s->opts.prealloc_align);
+
+    ret = bdrv_co_pwrite_zeroes(
+            bs->file, prealloc_start, prealloc_end - prealloc_start,
+            BDRV_REQ_NO_FALLBACK | BDRV_REQ_SERIALISING | BDRV_REQ_NO_WAIT);
+    if (ret < 0) {
+        s->file_end = ret;
+        return false;
+    }
+
+    s->file_end = prealloc_end;
+    return want_merge_zero;
+}
+
+static int coroutine_fn preallocate_co_pwrite_zeroes(BlockDriverState *bs,
+        int64_t offset, int bytes, BdrvRequestFlags flags)
+{
+    bool want_merge_zero =
+        !(flags & ~(BDRV_REQ_ZERO_WRITE | BDRV_REQ_NO_FALLBACK));
+    if (handle_write(bs, offset, bytes, want_merge_zero)) {
+        return 0;
+    }
+
+    return bdrv_co_pwrite_zeroes(bs->file, offset, bytes, flags);
+}
+
+static coroutine_fn int preallocate_co_pwritev_part(BlockDriverState *bs,
+                                                    uint64_t offset,
+                                                    uint64_t bytes,
+                                                    QEMUIOVector *qiov,
+                                                    size_t qiov_offset,
+                                                    int flags)
+{
+    handle_write(bs, offset, bytes, false);
+
+    return bdrv_co_pwritev_part(bs->file, offset, bytes, qiov, qiov_offset,
+                                flags);
+}
+
+static int coroutine_fn
+preallocate_co_truncate(BlockDriverState *bs, int64_t offset,
+                        bool exact, PreallocMode prealloc,
+                        BdrvRequestFlags flags, Error **errp)
+{
+    ERRP_GUARD();
+    BDRVPreallocateState *s = bs->opaque;
+    int ret;
+
+    if (s->data_end >= 0 && offset > s->data_end) {
+        if (s->file_end < 0) {
+            s->file_end = bdrv_getlength(bs->file->bs);
+            if (s->file_end < 0) {
+                error_setg(errp, "failed to get file length");
+                return s->file_end;
+            }
+        }
+
+        if (prealloc == PREALLOC_MODE_FALLOC) {
+            /*
+             * If offset <= s->file_end, the task is already done, just
+             * update s->file_end, to move part of "filter preallocation"
+             * to "preallocation requested by user".
+             * Otherwise just proceed to preallocate missing part.
+             */
+            if (offset <= s->file_end) {
+                s->data_end = offset;
+                return 0;
+            }
+        } else {
+            /*
+             * We have to drop our preallocation, to
+             * - avoid "Cannot use preallocation for shrinking files" in
+             *   case of offset < file_end
+             * - give PREALLOC_MODE_OFF a chance to keep small disk
+             *   usage
+             * - give PREALLOC_MODE_FULL a chance to actually write the
+             *   whole region as user expects
+             */
+            if (s->file_end > s->data_end) {
+                ret = bdrv_co_truncate(bs->file, s->data_end, true,
+                                       PREALLOC_MODE_OFF, 0, errp);
+                if (ret < 0) {
+                    s->file_end = ret;
+                    error_prepend(errp, "preallocate-filter: failed to drop "
+                                  "write-zero preallocation: ");
+                    return ret;
+                }
+                s->file_end = s->data_end;
+            }
+        }
+
+        s->data_end = offset;
+    }
+
+    ret = bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp);
+    if (ret < 0) {
+        s->file_end = s->zero_start = s->data_end = ret;
+        return ret;
+    }
+
+    if (has_prealloc_perms(bs)) {
+        s->file_end = s->zero_start = s->data_end = offset;
+    }
+    return 0;
+}
+
+static int coroutine_fn preallocate_co_flush(BlockDriverState *bs)
+{
+    return bdrv_co_flush(bs->file->bs);
+}
+
+static int64_t preallocate_getlength(BlockDriverState *bs)
+{
+    int64_t ret;
+    BDRVPreallocateState *s = bs->opaque;
+
+    if (s->data_end >= 0) {
+        return s->data_end;
+    }
+
+    ret = bdrv_getlength(bs->file->bs);
+
+    if (has_prealloc_perms(bs)) {
+        s->file_end = s->zero_start = s->data_end = ret;
+    }
+
+    return ret;
+}
+
+static int preallocate_check_perm(BlockDriverState *bs,
+                                  uint64_t perm, uint64_t shared, Error **errp)
+{
+    BDRVPreallocateState *s = bs->opaque;
+
+    if (s->data_end >= 0 && !can_write_resize(perm)) {
+        /*
+         * Lose permissions.
+         * We should truncate in check_perm, as in set_perm bs->file->perm will
+         * be already changed, and we should not violate it.
+         */
+        if (s->file_end < 0) {
+            s->file_end = bdrv_getlength(bs->file->bs);
+            if (s->file_end < 0) {
+                error_setg(errp, "Failed to get file length");
+                return s->file_end;
+            }
+        }
+
+        if (s->data_end < s->file_end) {
+            int ret = bdrv_truncate(bs->file, s->data_end, true,
+                                    PREALLOC_MODE_OFF, 0, NULL);
+            if (ret < 0) {
+                error_setg(errp, "Failed to drop preallocation");
+                s->file_end = ret;
+                return ret;
+            }
+            s->file_end = s->data_end;
+        }
+    }
+
+    return 0;
+}
+
+static void preallocate_set_perm(BlockDriverState *bs,
+                                 uint64_t perm, uint64_t shared)
+{
+    BDRVPreallocateState *s = bs->opaque;
+
+    if (can_write_resize(perm)) {
+        if (s->data_end < 0) {
+            s->data_end = s->file_end = s->zero_start =
+                bdrv_getlength(bs->file->bs);
+        }
+    } else {
+        /*
+         * We drop our permissions, as well as allow shared
+         * permissions (see preallocate_child_perm), anyone will be able to
+         * change the child, so mark all states invalid. We'll regain control if
+         * get good permissions back.
+         */
+        s->data_end = s->file_end = s->zero_start = -EINVAL;
+    }
+}
+
+static void preallocate_child_perm(BlockDriverState *bs, BdrvChild *c,
+    BdrvChildRole role, BlockReopenQueue *reopen_queue,
+    uint64_t perm, uint64_t shared, uint64_t *nperm, uint64_t *nshared)
+{
+    bdrv_default_perms(bs, c, role, reopen_queue, perm, shared, nperm, nshared);
+
+    if (can_write_resize(perm)) {
+        /* This should come by default, but let's enforce: */
+        *nperm |= BLK_PERM_WRITE | BLK_PERM_RESIZE;
+
+        /*
+         * Don't share, to keep our states s->file_end, s->data_end and
+         * s->zero_start valid.
+         */
+        *nshared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
+    }
+}
+
+BlockDriver bdrv_preallocate_filter = {
+    .format_name = "preallocate",
+    .instance_size = sizeof(BDRVPreallocateState),
+
+    .bdrv_getlength = preallocate_getlength,
+    .bdrv_open = preallocate_open,
+    .bdrv_close = preallocate_close,
+
+    .bdrv_reopen_prepare  = preallocate_reopen_prepare,
+    .bdrv_reopen_commit   = preallocate_reopen_commit,
+    .bdrv_reopen_abort    = preallocate_reopen_abort,
+
+    .bdrv_co_preadv_part = preallocate_co_preadv_part,
+    .bdrv_co_pwritev_part = preallocate_co_pwritev_part,
+    .bdrv_co_pwrite_zeroes = preallocate_co_pwrite_zeroes,
+    .bdrv_co_pdiscard = preallocate_co_pdiscard,
+    .bdrv_co_flush = preallocate_co_flush,
+    .bdrv_co_truncate = preallocate_co_truncate,
+
+    .bdrv_check_perm = preallocate_check_perm,
+    .bdrv_set_perm = preallocate_set_perm,
+    .bdrv_child_perm = preallocate_child_perm,
+
+    .has_variable_length = true,
+    .is_filter = true,
+};
+
+static void bdrv_preallocate_init(void)
+{
+    bdrv_register(&bdrv_preallocate_filter);
+}
+
+block_init(bdrv_preallocate_init);
diff --git a/block/meson.build b/block/meson.build
index 78e8b25232..9104e4d470 100644
--- a/block/meson.build
+++ b/block/meson.build
@@ -13,6 +13,7 @@ block_ss.add(files(
   'block-copy.c',
   'commit.c',
   'copy-on-read.c',
+  'preallocate.c',
   'create.c',
   'crypto.c',
   'dirty-bitmap.c',
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 09/21] qemu-io: add preallocate mode parameter for truncate command
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (7 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 08/21] block: introduce preallocate filter Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-10-21 14:58 ` [PATCH v7 10/21] iotests: qemu_io_silent: support --image-opts Vladimir Sementsov-Ogievskiy
                   ` (12 subsequent siblings)
  21 siblings, 0 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

This will be used in further test.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
---
 qemu-io-cmds.c | 46 ++++++++++++++++++++++++++++++++--------------
 1 file changed, 32 insertions(+), 14 deletions(-)

diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index 4153f1c0b0..97611969cb 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -1698,13 +1698,42 @@ static const cmdinfo_t flush_cmd = {
     .oneline    = "flush all in-core file state to disk",
 };
 
+static int truncate_f(BlockBackend *blk, int argc, char **argv);
+static const cmdinfo_t truncate_cmd = {
+    .name       = "truncate",
+    .altname    = "t",
+    .cfunc      = truncate_f,
+    .perm       = BLK_PERM_WRITE | BLK_PERM_RESIZE,
+    .argmin     = 1,
+    .argmax     = 3,
+    .args       = "[-m prealloc_mode] off",
+    .oneline    = "truncates the current file at the given offset",
+};
+
 static int truncate_f(BlockBackend *blk, int argc, char **argv)
 {
     Error *local_err = NULL;
     int64_t offset;
-    int ret;
+    int c, ret;
+    PreallocMode prealloc = PREALLOC_MODE_OFF;
 
-    offset = cvtnum(argv[1]);
+    while ((c = getopt(argc, argv, "m:")) != -1) {
+        switch (c) {
+        case 'm':
+            prealloc = qapi_enum_parse(&PreallocMode_lookup, optarg,
+                                       PREALLOC_MODE__MAX, NULL);
+            if (prealloc == PREALLOC_MODE__MAX) {
+                error_report("Invalid preallocation mode '%s'", optarg);
+                return -EINVAL;
+            }
+            break;
+        default:
+            qemuio_command_usage(&truncate_cmd);
+            return -EINVAL;
+        }
+    }
+
+    offset = cvtnum(argv[optind]);
     if (offset < 0) {
         print_cvtnum_err(offset, argv[1]);
         return offset;
@@ -1715,7 +1744,7 @@ static int truncate_f(BlockBackend *blk, int argc, char **argv)
      * exact=true.  It is better to err on the "emit more errors" side
      * than to be overly permissive.
      */
-    ret = blk_truncate(blk, offset, false, PREALLOC_MODE_OFF, 0, &local_err);
+    ret = blk_truncate(blk, offset, false, prealloc, 0, &local_err);
     if (ret < 0) {
         error_report_err(local_err);
         return ret;
@@ -1724,17 +1753,6 @@ static int truncate_f(BlockBackend *blk, int argc, char **argv)
     return 0;
 }
 
-static const cmdinfo_t truncate_cmd = {
-    .name       = "truncate",
-    .altname    = "t",
-    .cfunc      = truncate_f,
-    .perm       = BLK_PERM_WRITE | BLK_PERM_RESIZE,
-    .argmin     = 1,
-    .argmax     = 1,
-    .args       = "off",
-    .oneline    = "truncates the current file at the given offset",
-};
-
 static int length_f(BlockBackend *blk, int argc, char **argv)
 {
     int64_t size;
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 10/21] iotests: qemu_io_silent: support --image-opts
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (8 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 09/21] qemu-io: add preallocate mode parameter for truncate command Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-10-21 14:58 ` [PATCH v7 11/21] iotests.py: execute_setup_common(): add required_fmts argument Vladimir Sementsov-Ogievskiy
                   ` (11 subsequent siblings)
  21 siblings, 0 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
Reviewed-by: Max Reitz <mreitz@redhat.com>
---
 tests/qemu-iotests/iotests.py | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index f212cec446..0e3d1d0ba3 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -205,7 +205,12 @@ def qemu_io_log(*args):
 
 def qemu_io_silent(*args):
     '''Run qemu-io and return the exit code, suppressing stdout'''
-    args = qemu_io_args + list(args)
+    if '-f' in args or '--image-opts' in args:
+        default_args = qemu_io_args_no_fmt
+    else:
+        default_args = qemu_io_args
+
+    args = default_args + list(args)
     exitcode = subprocess.call(args, stdout=open('/dev/null', 'w'))
     if exitcode < 0:
         sys.stderr.write('qemu-io received signal %i: %s\n' %
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 11/21] iotests.py: execute_setup_common(): add required_fmts argument
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (9 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 10/21] iotests: qemu_io_silent: support --image-opts Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-11-13 14:42   ` Max Reitz
  2020-10-21 14:58 ` [PATCH v7 12/21] iotests: add 298 to test new preallocate filter driver Vladimir Sementsov-Ogievskiy
                   ` (10 subsequent siblings)
  21 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Add a parameter to skip test if some needed additional formats are not
supported (for example filter drivers).

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 tests/qemu-iotests/iotests.py | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
index 0e3d1d0ba3..274396b14e 100644
--- a/tests/qemu-iotests/iotests.py
+++ b/tests/qemu-iotests/iotests.py
@@ -1109,6 +1109,11 @@ def _verify_aio_mode(supported_aio_modes: Sequence[str] = ()) -> None:
     if supported_aio_modes and (aiomode not in supported_aio_modes):
         notrun('not suitable for this aio mode: %s' % aiomode)
 
+def _verify_formats(required_formats: Sequence[str] = ()) -> None:
+    usf_list = list(set(required_formats) - set(supported_formats()))
+    if usf_list:
+        notrun(f'formats {usf_list} are not whitelisted')
+
 def supports_quorum():
     return 'quorum' in qemu_img_pipe('--help')
 
@@ -1266,7 +1271,8 @@ def execute_setup_common(supported_fmts: Sequence[str] = (),
                          supported_aio_modes: Sequence[str] = (),
                          unsupported_fmts: Sequence[str] = (),
                          supported_protocols: Sequence[str] = (),
-                         unsupported_protocols: Sequence[str] = ()) -> bool:
+                         unsupported_protocols: Sequence[str] = (),
+                         required_fmts: Sequence[str] = ()) -> bool:
     """
     Perform necessary setup for either script-style or unittest-style tests.
 
@@ -1292,6 +1298,7 @@ def execute_setup_common(supported_fmts: Sequence[str] = (),
     _verify_platform(supported=supported_platforms)
     _verify_cache_mode(supported_cache_modes)
     _verify_aio_mode(supported_aio_modes)
+    _verify_formats(required_fmts)
 
     return debug
 
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 12/21] iotests: add 298 to test new preallocate filter driver
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (10 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 11/21] iotests.py: execute_setup_common(): add required_fmts argument Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-11-13 14:43   ` Max Reitz
  2020-10-21 14:58 ` [PATCH v7 13/21] scripts/simplebench: fix grammar: s/successed/succeeded/ Vladimir Sementsov-Ogievskiy
                   ` (9 subsequent siblings)
  21 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 tests/qemu-iotests/298     | 186 +++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/298.out |   5 +
 tests/qemu-iotests/group   |   1 +
 3 files changed, 192 insertions(+)
 create mode 100644 tests/qemu-iotests/298
 create mode 100644 tests/qemu-iotests/298.out

diff --git a/tests/qemu-iotests/298 b/tests/qemu-iotests/298
new file mode 100644
index 0000000000..d535946b5f
--- /dev/null
+++ b/tests/qemu-iotests/298
@@ -0,0 +1,186 @@
+#!/usr/bin/env python3
+#
+# Test for preallocate filter
+#
+# Copyright (c) 2020 Virtuozzo International GmbH.
+#
+# 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/>.
+#
+
+import os
+import iotests
+
+MiB = 1024 * 1024
+disk = os.path.join(iotests.test_dir, 'disk')
+overlay = os.path.join(iotests.test_dir, 'overlay')
+refdisk = os.path.join(iotests.test_dir, 'refdisk')
+drive_opts = f'node-name=disk,driver={iotests.imgfmt},' \
+    f'file.node-name=filter,file.driver=preallocate,' \
+    f'file.file.node-name=file,file.file.filename={disk}'
+
+
+class TestPreallocateBase(iotests.QMPTestCase):
+    def setUp(self):
+        iotests.qemu_img_create('-f', iotests.imgfmt, disk, str(10 * MiB))
+
+    def tearDown(self):
+        try:
+            self.check_small()
+            check = iotests.qemu_img_check(disk)
+            self.assertFalse('leaks' in check)
+            self.assertFalse('corruptions' in check)
+            self.assertEqual(check['check-errors'], 0)
+        finally:
+            os.remove(disk)
+
+    def check_big(self):
+        self.assertTrue(os.path.getsize(disk) > 100 * MiB)
+
+    def check_small(self):
+        self.assertTrue(os.path.getsize(disk) < 10 * MiB)
+
+
+class TestQemuImg(TestPreallocateBase):
+    def test_qemu_img(self):
+        p = iotests.QemuIoInteractive('--image-opts', drive_opts)
+
+        p.cmd('write 0 1M')
+        p.cmd('flush')
+
+        self.check_big()
+
+        p.close()
+
+
+class TestPreallocateFilter(TestPreallocateBase):
+    def setUp(self):
+        super().setUp()
+        self.vm = iotests.VM().add_drive(path=None, opts=drive_opts)
+        self.vm.launch()
+
+    def tearDown(self):
+        self.vm.shutdown()
+        super().tearDown()
+
+    def test_prealloc(self):
+        self.vm.hmp_qemu_io('drive0', 'write 0 1M')
+        self.check_big()
+
+    def test_external_snapshot(self):
+        self.test_prealloc()
+
+        result = self.vm.qmp('blockdev-snapshot-sync', node_name='disk',
+                             snapshot_file=overlay,
+                             snapshot_node_name='overlay')
+        self.assert_qmp(result, 'return', {})
+
+        # on reopen to  r-o base preallocation should be dropped
+        self.check_small()
+
+        self.vm.hmp_qemu_io('drive0', 'write 1M 1M')
+
+        result = self.vm.qmp('block-commit', device='overlay')
+        self.assert_qmp(result, 'return', {})
+        self.complete_and_wait()
+
+        # commit of new megabyte should trigger preallocation
+        self.check_big()
+
+    def test_reopen_opts(self):
+        result = self.vm.qmp('x-blockdev-reopen', **{
+            'node-name': 'disk',
+            'driver': iotests.imgfmt,
+            'file': {
+                'node-name': 'filter',
+                'driver': 'preallocate',
+                'prealloc-size': 20 * MiB,
+                'prealloc-align': 5 * MiB,
+                'file': {
+                    'node-name': 'file',
+                    'driver': 'file',
+                    'filename': disk
+                }
+            }
+        })
+        self.assert_qmp(result, 'return', {})
+
+        self.vm.hmp_qemu_io('drive0', 'write 0 1M')
+        self.assertTrue(os.path.getsize(disk) == 25 * MiB)
+
+
+class TestTruncate(iotests.QMPTestCase):
+    def setUp(self):
+        iotests.qemu_img_create('-f', iotests.imgfmt, disk, str(10 * MiB))
+        iotests.qemu_img_create('-f', iotests.imgfmt, refdisk, str(10 * MiB))
+
+    def tearDown(self):
+        os.remove(disk)
+        os.remove(refdisk)
+
+    def do_test(self, prealloc_mode, new_size):
+        ret = iotests.qemu_io_silent('--image-opts', '-c', 'write 0 10M', '-c',
+                                     f'truncate -m {prealloc_mode} {new_size}',
+                                     drive_opts)
+        self.assertEqual(ret, 0)
+
+        ret = iotests.qemu_io_silent('-f', iotests.imgfmt, '-c', 'write 0 10M',
+                                     '-c',
+                                     f'truncate -m {prealloc_mode} {new_size}',
+                                     refdisk)
+        self.assertEqual(ret, 0)
+
+        stat = os.stat(disk)
+        refstat = os.stat(refdisk)
+
+        # Probably we'll want preallocate filter to keep align to cluster when
+        # shrink preallocation, so, ignore small differece
+        self.assertLess(abs(stat.st_size - refstat.st_size), 64 * 1024)
+
+        # Preallocate filter may leak some internal clusters (for example, if
+        # guest write far over EOF, skipping some clusters - they will remain
+        # fallocated, preallocate filter don't care about such leaks, it drops
+        # only trailing preallocation.
+        self.assertLess(abs(stat.st_blocks - refstat.st_blocks) * 512,
+                        1024 * 1024)
+
+    def test_real_shrink(self):
+        self.do_test('off', '5M')
+
+    def test_truncate_inside_preallocated_area__falloc(self):
+        self.do_test('falloc', '50M')
+
+    def test_truncate_inside_preallocated_area__metadata(self):
+        self.do_test('metadata', '50M')
+
+    def test_truncate_inside_preallocated_area__full(self):
+        self.do_test('full', '50M')
+
+    def test_truncate_inside_preallocated_area__off(self):
+        self.do_test('off', '50M')
+
+    def test_truncate_over_preallocated_area__falloc(self):
+        self.do_test('falloc', '150M')
+
+    def test_truncate_over_preallocated_area__metadata(self):
+        self.do_test('metadata', '150M')
+
+    def test_truncate_over_preallocated_area__full(self):
+        self.do_test('full', '150M')
+
+    def test_truncate_over_preallocated_area__off(self):
+        self.do_test('off', '150M')
+
+
+if __name__ == '__main__':
+    iotests.main(supported_fmts=['qcow2'], required_fmts=['preallocate'])
diff --git a/tests/qemu-iotests/298.out b/tests/qemu-iotests/298.out
new file mode 100644
index 0000000000..fa16b5ccef
--- /dev/null
+++ b/tests/qemu-iotests/298.out
@@ -0,0 +1,5 @@
+.............
+----------------------------------------------------------------------
+Ran 13 tests
+
+OK
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 3432989283..43da33337e 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -307,6 +307,7 @@
 295 rw
 296 rw
 297 meta
+298
 299 auto quick
 300 migration
 301 backing quick
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 13/21] scripts/simplebench: fix grammar: s/successed/succeeded/
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (11 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 12/21] iotests: add 298 to test new preallocate filter driver Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-11-13 14:44   ` Max Reitz
  2020-10-21 14:58 ` [PATCH v7 14/21] scripts/simplebench: support iops Vladimir Sementsov-Ogievskiy
                   ` (8 subsequent siblings)
  21 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 scripts/simplebench/simplebench.py | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/scripts/simplebench/simplebench.py b/scripts/simplebench/simplebench.py
index 59e7314ff6..2445932fc2 100644
--- a/scripts/simplebench/simplebench.py
+++ b/scripts/simplebench/simplebench.py
@@ -54,14 +54,14 @@ def bench_one(test_func, test_env, test_case, count=5, initial_run=True):
 
     result = {'runs': runs}
 
-    successed = [r for r in runs if ('seconds' in r)]
-    if successed:
-        avg = sum(r['seconds'] for r in successed) / len(successed)
+    succeeded = [r for r in runs if ('seconds' in r)]
+    if succeeded:
+        avg = sum(r['seconds'] for r in succeeded) / len(succeeded)
         result['average'] = avg
-        result['delta'] = max(abs(r['seconds'] - avg) for r in successed)
+        result['delta'] = max(abs(r['seconds'] - avg) for r in succeeded)
 
-    if len(successed) < count:
-        result['n-failed'] = count - len(successed)
+    if len(succeeded) < count:
+        result['n-failed'] = count - len(succeeded)
 
     return result
 
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 14/21] scripts/simplebench: support iops
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (12 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 13/21] scripts/simplebench: fix grammar: s/successed/succeeded/ Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-11-13 15:02   ` Max Reitz
  2020-10-21 14:58 ` [PATCH v7 15/21] scripts/simplebench: use standard deviation for +- error Vladimir Sementsov-Ogievskiy
                   ` (7 subsequent siblings)
  21 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Support benchmarks returning not seconds but iops. We'll use it for
further new test.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 scripts/simplebench/simplebench.py | 38 ++++++++++++++++++++++--------
 1 file changed, 28 insertions(+), 10 deletions(-)

diff --git a/scripts/simplebench/simplebench.py b/scripts/simplebench/simplebench.py
index 2445932fc2..2251cd34ea 100644
--- a/scripts/simplebench/simplebench.py
+++ b/scripts/simplebench/simplebench.py
@@ -24,9 +24,12 @@ def bench_one(test_func, test_env, test_case, count=5, initial_run=True):
 
     test_func   -- benchmarking function with prototype
                    test_func(env, case), which takes test_env and test_case
-                   arguments and returns {'seconds': int} (which is benchmark
-                   result) on success and {'error': str} on error. Returned
-                   dict may contain any other additional fields.
+                   arguments and on success returns dict with 'seconds' or
+                   'iops' (or both) fields, specifying the benchmark result.
+                   If both 'iops' and 'seconds' provided, the 'iops' is
+                   considered the main, and 'seconds' is just an additional
+                   info. On failure test_func should return {'error': str}.
+                   Returned dict may contain any other additional fields.
     test_env    -- test environment - opaque first argument for test_func
     test_case   -- test case - opaque second argument for test_func
     count       -- how many times to call test_func, to calculate average
@@ -34,8 +37,9 @@ def bench_one(test_func, test_env, test_case, count=5, initial_run=True):
 
     Returns dict with the following fields:
         'runs':     list of test_func results
-        'average':  average seconds per run (exists only if at least one run
-                    succeeded)
+        'dimension': dimension of results, may be 'seconds' or 'iops'
+        'average':  average value (iops or seconds) per run (exists only if at
+                    least one run succeeded)
         'delta':    maximum delta between test_func result and the average
                     (exists only if at least one run succeeded)
         'n-failed': number of failed runs (exists only if at least one run
@@ -54,11 +58,19 @@ def bench_one(test_func, test_env, test_case, count=5, initial_run=True):
 
     result = {'runs': runs}
 
-    succeeded = [r for r in runs if ('seconds' in r)]
+    succeeded = [r for r in runs if ('seconds' in r or 'iops' in r)]
     if succeeded:
-        avg = sum(r['seconds'] for r in succeeded) / len(succeeded)
+        if 'iops' in succeeded[0]:
+            assert all('iops' in r for r in succeeded)
+            dim = 'iops'
+        else:
+            assert all('seconds' in r for r in succeeded)
+            assert all('iops' not in r for r in succeeded)
+            dim = 'seconds'
+        avg = sum(r[dim] for r in succeeded) / len(succeeded)
+        result['dimension'] = dim
         result['average'] = avg
-        result['delta'] = max(abs(r['seconds'] - avg) for r in succeeded)
+        result['delta'] = max(abs(r[dim] - avg) for r in succeeded)
 
     if len(succeeded) < count:
         result['n-failed'] = count - len(succeeded)
@@ -118,11 +130,17 @@ def ascii(results):
     """Return ASCII representation of bench() returned dict."""
     from tabulate import tabulate
 
+    dim = None
     tab = [[""] + [c['id'] for c in results['envs']]]
     for case in results['cases']:
         row = [case['id']]
         for env in results['envs']:
-            row.append(ascii_one(results['tab'][case['id']][env['id']]))
+            res = results['tab'][case['id']][env['id']]
+            if dim is None:
+                dim = res['dimension']
+            else:
+                assert dim == res['dimension']
+            row.append(ascii_one(res))
         tab.append(row)
 
-    return tabulate(tab)
+    return f'All results are in {dim}\n\n' + tabulate(tab)
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 15/21] scripts/simplebench: use standard deviation for +- error
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (13 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 14/21] scripts/simplebench: support iops Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-11-13 15:30   ` Max Reitz
  2020-10-21 14:58 ` [PATCH v7 16/21] simplebench: rename ascii() to results_to_text() Vladimir Sementsov-Ogievskiy
                   ` (6 subsequent siblings)
  21 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Standard deviation is more usual to see after +- than current maximum
of deviations.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 scripts/simplebench/simplebench.py | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/scripts/simplebench/simplebench.py b/scripts/simplebench/simplebench.py
index 2251cd34ea..55ec1ad5db 100644
--- a/scripts/simplebench/simplebench.py
+++ b/scripts/simplebench/simplebench.py
@@ -18,6 +18,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+import statistics
+
 
 def bench_one(test_func, test_env, test_case, count=5, initial_run=True):
     """Benchmark one test-case
@@ -40,7 +42,7 @@ def bench_one(test_func, test_env, test_case, count=5, initial_run=True):
         'dimension': dimension of results, may be 'seconds' or 'iops'
         'average':  average value (iops or seconds) per run (exists only if at
                     least one run succeeded)
-        'delta':    maximum delta between test_func result and the average
+        'stdev':    standard deviation of results
                     (exists only if at least one run succeeded)
         'n-failed': number of failed runs (exists only if at least one run
                     failed)
@@ -67,10 +69,9 @@ def bench_one(test_func, test_env, test_case, count=5, initial_run=True):
             assert all('seconds' in r for r in succeeded)
             assert all('iops' not in r for r in succeeded)
             dim = 'seconds'
-        avg = sum(r[dim] for r in succeeded) / len(succeeded)
         result['dimension'] = dim
-        result['average'] = avg
-        result['delta'] = max(abs(r[dim] - avg) for r in succeeded)
+        result['average'] = statistics.mean(r[dim] for r in succeeded)
+        result['stdev'] = statistics.stdev(r[dim] for r in succeeded)
 
     if len(succeeded) < count:
         result['n-failed'] = count - len(succeeded)
@@ -81,7 +82,7 @@ def bench_one(test_func, test_env, test_case, count=5, initial_run=True):
 def ascii_one(result):
     """Return ASCII representation of bench_one() returned dict."""
     if 'average' in result:
-        s = '{:.2f} +- {:.2f}'.format(result['average'], result['delta'])
+        s = '{:.2f} +- {:.2f}'.format(result['average'], result['stdev'])
         if 'n-failed' in result:
             s += '\n({} failed)'.format(result['n-failed'])
         return s
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 16/21] simplebench: rename ascii() to results_to_text()
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (14 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 15/21] scripts/simplebench: use standard deviation for +- error Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-11-13 15:31   ` Max Reitz
  2020-10-21 14:58 ` [PATCH v7 17/21] simplebench: move results_to_text() into separate file Vladimir Sementsov-Ogievskiy
                   ` (5 subsequent siblings)
  21 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Next patch will use utf8 plus-minus symbol, let's use more generic (and
more readable) name.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 scripts/simplebench/bench-example.py   |  2 +-
 scripts/simplebench/bench_write_req.py |  2 +-
 scripts/simplebench/simplebench.py     | 10 +++++-----
 3 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/scripts/simplebench/bench-example.py b/scripts/simplebench/bench-example.py
index c642a5b891..f24cf22fe9 100644
--- a/scripts/simplebench/bench-example.py
+++ b/scripts/simplebench/bench-example.py
@@ -77,4 +77,4 @@ test_envs = [
 ]
 
 result = simplebench.bench(bench_func, test_envs, test_cases, count=3)
-print(simplebench.ascii(result))
+print(simplebench.results_to_text(result))
diff --git a/scripts/simplebench/bench_write_req.py b/scripts/simplebench/bench_write_req.py
index ca1178fd68..e175bcd7a4 100755
--- a/scripts/simplebench/bench_write_req.py
+++ b/scripts/simplebench/bench_write_req.py
@@ -167,4 +167,4 @@ if __name__ == '__main__':
 
     result = simplebench.bench(bench_func, test_envs, test_cases, count=3,
                                initial_run=False)
-    print(simplebench.ascii(result))
+    print(simplebench.results_to_text(result))
diff --git a/scripts/simplebench/simplebench.py b/scripts/simplebench/simplebench.py
index 55ec1ad5db..aa74b78a04 100644
--- a/scripts/simplebench/simplebench.py
+++ b/scripts/simplebench/simplebench.py
@@ -79,8 +79,8 @@ def bench_one(test_func, test_env, test_case, count=5, initial_run=True):
     return result
 
 
-def ascii_one(result):
-    """Return ASCII representation of bench_one() returned dict."""
+def result_to_text(result):
+    """Return text representation of bench_one() returned dict."""
     if 'average' in result:
         s = '{:.2f} +- {:.2f}'.format(result['average'], result['stdev'])
         if 'n-failed' in result:
@@ -127,8 +127,8 @@ def bench(test_func, test_envs, test_cases, *args, **vargs):
     return results
 
 
-def ascii(results):
-    """Return ASCII representation of bench() returned dict."""
+def results_to_text(results):
+    """Return text representation of bench() returned dict."""
     from tabulate import tabulate
 
     dim = None
@@ -141,7 +141,7 @@ def ascii(results):
                 dim = res['dimension']
             else:
                 assert dim == res['dimension']
-            row.append(ascii_one(res))
+            row.append(result_to_text(res))
         tab.append(row)
 
     return f'All results are in {dim}\n\n' + tabulate(tab)
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 17/21] simplebench: move results_to_text() into separate file
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (15 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 16/21] simplebench: rename ascii() to results_to_text() Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-11-13 15:39   ` Max Reitz
  2020-10-21 14:58 ` [PATCH v7 18/21] simplebench/results_to_text: improve view of the table Vladimir Sementsov-Ogievskiy
                   ` (4 subsequent siblings)
  21 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Let's keep view part in separate: this way it's better to improve it in
the following commits.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 scripts/simplebench/bench-example.py   |  3 +-
 scripts/simplebench/bench_write_req.py |  3 +-
 scripts/simplebench/results_to_text.py | 48 ++++++++++++++++++++++++++
 scripts/simplebench/simplebench.py     | 31 -----------------
 4 files changed, 52 insertions(+), 33 deletions(-)
 create mode 100644 scripts/simplebench/results_to_text.py

diff --git a/scripts/simplebench/bench-example.py b/scripts/simplebench/bench-example.py
index f24cf22fe9..d9c7f7bc17 100644
--- a/scripts/simplebench/bench-example.py
+++ b/scripts/simplebench/bench-example.py
@@ -19,6 +19,7 @@
 #
 
 import simplebench
+from results_to_text import results_to_text
 from bench_block_job import bench_block_copy, drv_file, drv_nbd
 
 
@@ -77,4 +78,4 @@ test_envs = [
 ]
 
 result = simplebench.bench(bench_func, test_envs, test_cases, count=3)
-print(simplebench.results_to_text(result))
+print(results_to_text(result))
diff --git a/scripts/simplebench/bench_write_req.py b/scripts/simplebench/bench_write_req.py
index e175bcd7a4..da601ea2fe 100755
--- a/scripts/simplebench/bench_write_req.py
+++ b/scripts/simplebench/bench_write_req.py
@@ -26,6 +26,7 @@ import sys
 import os
 import subprocess
 import simplebench
+from results_to_text import results_to_text
 
 
 def bench_func(env, case):
@@ -167,4 +168,4 @@ if __name__ == '__main__':
 
     result = simplebench.bench(bench_func, test_envs, test_cases, count=3,
                                initial_run=False)
-    print(simplebench.results_to_text(result))
+    print(results_to_text(result))
diff --git a/scripts/simplebench/results_to_text.py b/scripts/simplebench/results_to_text.py
new file mode 100644
index 0000000000..58d909ffd9
--- /dev/null
+++ b/scripts/simplebench/results_to_text.py
@@ -0,0 +1,48 @@
+# Simple benchmarking framework
+#
+# Copyright (c) 2019 Virtuozzo International GmbH.
+#
+# 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/>.
+#
+
+
+def result_to_text(result):
+    """Return text representation of bench_one() returned dict."""
+    if 'average' in result:
+        s = '{:.2f} +- {:.2f}'.format(result['average'], result['stdev'])
+        if 'n-failed' in result:
+            s += '\n({} failed)'.format(result['n-failed'])
+        return s
+    else:
+        return 'FAILED'
+
+
+def results_to_text(results):
+    """Return text representation of bench() returned dict."""
+    from tabulate import tabulate
+
+    dim = None
+    tab = [[""] + [c['id'] for c in results['envs']]]
+    for case in results['cases']:
+        row = [case['id']]
+        for env in results['envs']:
+            res = results['tab'][case['id']][env['id']]
+            if dim is None:
+                dim = res['dimension']
+            else:
+                assert dim == res['dimension']
+            row.append(result_to_text(res))
+        tab.append(row)
+
+    return f'All results are in {dim}\n\n' + tabulate(tab)
diff --git a/scripts/simplebench/simplebench.py b/scripts/simplebench/simplebench.py
index aa74b78a04..f61513af90 100644
--- a/scripts/simplebench/simplebench.py
+++ b/scripts/simplebench/simplebench.py
@@ -79,17 +79,6 @@ def bench_one(test_func, test_env, test_case, count=5, initial_run=True):
     return result
 
 
-def result_to_text(result):
-    """Return text representation of bench_one() returned dict."""
-    if 'average' in result:
-        s = '{:.2f} +- {:.2f}'.format(result['average'], result['stdev'])
-        if 'n-failed' in result:
-            s += '\n({} failed)'.format(result['n-failed'])
-        return s
-    else:
-        return 'FAILED'
-
-
 def bench(test_func, test_envs, test_cases, *args, **vargs):
     """Fill benchmark table
 
@@ -125,23 +114,3 @@ def bench(test_func, test_envs, test_cases, *args, **vargs):
 
     print('Done')
     return results
-
-
-def results_to_text(results):
-    """Return text representation of bench() returned dict."""
-    from tabulate import tabulate
-
-    dim = None
-    tab = [[""] + [c['id'] for c in results['envs']]]
-    for case in results['cases']:
-        row = [case['id']]
-        for env in results['envs']:
-            res = results['tab'][case['id']][env['id']]
-            if dim is None:
-                dim = res['dimension']
-            else:
-                assert dim == res['dimension']
-            row.append(result_to_text(res))
-        tab.append(row)
-
-    return f'All results are in {dim}\n\n' + tabulate(tab)
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 18/21] simplebench/results_to_text: improve view of the table
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (16 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 17/21] simplebench: move results_to_text() into separate file Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-11-13 15:59   ` Max Reitz
  2020-10-21 14:58 ` [PATCH v7 19/21] simplebench/results_to_text: add difference line to " Vladimir Sementsov-Ogievskiy
                   ` (3 subsequent siblings)
  21 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Move to generic format for floats and percentage for error.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 scripts/simplebench/results_to_text.py | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/scripts/simplebench/results_to_text.py b/scripts/simplebench/results_to_text.py
index 58d909ffd9..479f7ac1d4 100644
--- a/scripts/simplebench/results_to_text.py
+++ b/scripts/simplebench/results_to_text.py
@@ -16,11 +16,22 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
+import math
+
+
+def format_value(x, stdev):
+    stdev_pr = stdev / x * 100
+    if stdev_pr < 1.5:
+        # don't care too much
+        return f'{x:.2g}'
+    else:
+        return f'{x:.2g} ± {math.ceil(stdev_pr)}%'
+
 
 def result_to_text(result):
     """Return text representation of bench_one() returned dict."""
     if 'average' in result:
-        s = '{:.2f} +- {:.2f}'.format(result['average'], result['stdev'])
+        s = format_value(result['average'], result['stdev'])
         if 'n-failed' in result:
             s += '\n({} failed)'.format(result['n-failed'])
         return s
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 19/21] simplebench/results_to_text: add difference line to the table
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (17 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 18/21] simplebench/results_to_text: improve view of the table Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-10-24 10:24   ` Vladimir Sementsov-Ogievskiy
  2020-11-13 16:21   ` Max Reitz
  2020-10-21 14:58 ` [PATCH v7 20/21] simplebench/results_to_text: make executable Vladimir Sementsov-Ogievskiy
                   ` (2 subsequent siblings)
  21 siblings, 2 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Performance improvements / degradations are usually discussed in
percentage. Let's make the script calculate it for us.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 scripts/simplebench/results_to_text.py | 67 +++++++++++++++++++++++---
 1 file changed, 60 insertions(+), 7 deletions(-)

diff --git a/scripts/simplebench/results_to_text.py b/scripts/simplebench/results_to_text.py
index 479f7ac1d4..56fdacf7ca 100644
--- a/scripts/simplebench/results_to_text.py
+++ b/scripts/simplebench/results_to_text.py
@@ -17,6 +17,10 @@
 #
 
 import math
+import tabulate
+
+# We want leading whitespace for difference row cells (see below)
+tabulate.PRESERVE_WHITESPACE = True
 
 
 def format_value(x, stdev):
@@ -39,21 +43,70 @@ def result_to_text(result):
         return 'FAILED'
 
 
-def results_to_text(results):
-    """Return text representation of bench() returned dict."""
-    from tabulate import tabulate
-
+def results_dimension(results):
     dim = None
-    tab = [[""] + [c['id'] for c in results['envs']]]
     for case in results['cases']:
-        row = [case['id']]
         for env in results['envs']:
             res = results['tab'][case['id']][env['id']]
             if dim is None:
                 dim = res['dimension']
             else:
                 assert dim == res['dimension']
+
+    assert dim in ('iops', 'sec')
+
+    return dim
+
+
+def results_to_text(results):
+    """Return text representation of bench() returned dict."""
+    n_columns = len(results['envs'])
+    named_columns = n_columns > 2
+    dim = results_dimension(results)
+    tab = []
+
+    if named_columns:
+        # Environment columns are named A, B, ...
+        tab.append([''] + [chr(ord('A') + i) for i in range(n_columns)])
+
+    tab.append([''] + [c['id'] for c in results['envs']])
+
+    for case in results['cases']:
+        row = [case['id']]
+        case_results = results['tab'][case['id']]
+        for env in results['envs']:
+            res = case_results[env['id']]
             row.append(result_to_text(res))
         tab.append(row)
 
-    return f'All results are in {dim}\n\n' + tabulate(tab)
+        # Add row of difference between columns. For each column starting from
+        # B we calculate difference with all previous columns.
+        row = ['', '']  # case name and first column
+        for i in range(1, n_columns):
+            cell = ''
+            env = results['envs'][i]
+            res = case_results[env['id']]
+
+            if 'average' not in res:
+                # Failed result
+                row.append(cell)
+                continue
+
+            for j in range(0, i):
+                env_j = results['envs'][j]
+                res_j = case_results[env_j['id']]
+                cell += ' '
+
+                if 'average' not in res_j:
+                    # Failed result
+                    cell += '--'
+                    continue
+
+                col_j = tab[0][j + 1] if named_columns else ''
+                diff_pr = round((res['average'] - res_j['average']) /
+                                res_j['average'] * 100)
+                cell += f' {col_j}{diff_pr:+}%'
+            row.append(cell)
+        tab.append(row)
+
+    return f'All results are in {dim}\n\n' + tabulate.tabulate(tab)
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 20/21] simplebench/results_to_text: make executable
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (18 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 19/21] simplebench/results_to_text: add difference line to " Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-11-13 16:22   ` Max Reitz
  2020-10-21 14:58 ` [PATCH v7 21/21] scripts/simplebench: add bench_prealloc.py Vladimir Sementsov-Ogievskiy
  2020-11-13 19:33 ` [PATCH v7 00/21] preallocate filter Max Reitz
  21 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Make results_to_text a tool to dump results saved in JSON file.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 scripts/simplebench/results_to_text.py | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 mode change 100644 => 100755 scripts/simplebench/results_to_text.py

diff --git a/scripts/simplebench/results_to_text.py b/scripts/simplebench/results_to_text.py
old mode 100644
new mode 100755
index 56fdacf7ca..db8ea41cf2
--- a/scripts/simplebench/results_to_text.py
+++ b/scripts/simplebench/results_to_text.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python3
+#
 # Simple benchmarking framework
 #
 # Copyright (c) 2019 Virtuozzo International GmbH.
@@ -110,3 +112,15 @@ def results_to_text(results):
         tab.append(row)
 
     return f'All results are in {dim}\n\n' + tabulate.tabulate(tab)
+
+
+if __name__ == '__main__':
+    import sys
+    import json
+
+    if len(sys.argv) < 2:
+        print(f'USAGE: {sys.argv[0]} results.json')
+        exit(1)
+
+    with open(sys.argv[1]) as f:
+        print(results_to_text(json.load(f)))
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v7 21/21] scripts/simplebench: add bench_prealloc.py
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (19 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 20/21] simplebench/results_to_text: make executable Vladimir Sementsov-Ogievskiy
@ 2020-10-21 14:58 ` Vladimir Sementsov-Ogievskiy
  2020-11-13 16:24   ` Max Reitz
  2020-11-13 19:33 ` [PATCH v7 00/21] preallocate filter Max Reitz
  21 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-21 14:58 UTC (permalink / raw)
  To: qemu-block
  Cc: qemu-devel, vsementsov, eblake, armbru, fam, stefanha, mreitz,
	kwolf, den

Benchmark for new preallocate filter.

Example usage:
    ./bench_prealloc.py ../../build/qemu-img \
        ssd-ext4:/path/to/mount/point \
        ssd-xfs:/path2 hdd-ext4:/path3 hdd-xfs:/path4

The benchmark shows performance improvement (or degradation) when use
new preallocate filter with qcow2 image.

Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
 scripts/simplebench/bench_prealloc.py | 132 ++++++++++++++++++++++++++
 1 file changed, 132 insertions(+)
 create mode 100755 scripts/simplebench/bench_prealloc.py

diff --git a/scripts/simplebench/bench_prealloc.py b/scripts/simplebench/bench_prealloc.py
new file mode 100755
index 0000000000..85f588c597
--- /dev/null
+++ b/scripts/simplebench/bench_prealloc.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python3
+#
+# Benchmark preallocate filter
+#
+# Copyright (c) 2020 Virtuozzo International GmbH.
+#
+# 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/>.
+#
+
+
+import sys
+import os
+import subprocess
+import re
+import json
+
+import simplebench
+from results_to_text import results_to_text
+
+
+def qemu_img_bench(args):
+    p = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
+                       universal_newlines=True)
+
+    if p.returncode == 0:
+        try:
+            m = re.search(r'Run completed in (\d+.\d+) seconds.', p.stdout)
+            return {'seconds': float(m.group(1))}
+        except Exception:
+            return {'error': f'failed to parse qemu-img output: {p.stdout}'}
+    else:
+        return {'error': f'qemu-img failed: {p.returncode}: {p.stdout}'}
+
+
+def bench_func(env, case):
+    fname = f"{case['dir']}/prealloc-test.qcow2"
+    try:
+        os.remove(fname)
+    except OSError:
+        pass
+
+    subprocess.run([env['qemu-img-binary'], 'create', '-f', 'qcow2', fname,
+                   '16G'], stdout=subprocess.DEVNULL,
+                   stderr=subprocess.DEVNULL, check=True)
+
+    args = [env['qemu-img-binary'], 'bench', '-c', str(case['count']),
+            '-d', '64', '-s', case['block-size'], '-t', 'none', '-n', '-w']
+    if env['prealloc']:
+        args += ['--image-opts',
+                 'driver=qcow2,file.driver=preallocate,file.file.driver=file,'
+                 f'file.file.filename={fname}']
+    else:
+        args += ['-f', 'qcow2', fname]
+
+    return qemu_img_bench(args)
+
+
+def auto_count_bench_func(env, case):
+    case['count'] = 100
+    while True:
+        res = bench_func(env, case)
+        if 'error' in res:
+            return res
+
+        if res['seconds'] >= 1:
+            break
+
+        case['count'] *= 10
+
+    if res['seconds'] < 5:
+        case['count'] = round(case['count'] * 5 / res['seconds'])
+        res = bench_func(env, case)
+        if 'error' in res:
+            return res
+
+    res['iops'] = case['count'] / res['seconds']
+    return res
+
+
+if __name__ == '__main__':
+    if len(sys.argv) < 2:
+        print(f'USAGE: {sys.argv[0]} <qemu-img binary> '
+              'DISK_NAME:DIR_PATH ...')
+        exit(1)
+
+    qemu_img = sys.argv[1]
+
+    envs = [
+        {
+            'id': 'no-prealloc',
+            'qemu-img-binary': qemu_img,
+            'prealloc': False
+        },
+        {
+            'id': 'prealloc',
+            'qemu-img-binary': qemu_img,
+            'prealloc': True
+        }
+    ]
+
+    aligned_cases = []
+    unaligned_cases = []
+
+    for disk in sys.argv[2:]:
+        name, path = disk.split(':')
+        aligned_cases.append({
+            'id': f'{name}, aligned sequential 16k',
+            'block-size': '16k',
+            'dir': path
+        })
+        unaligned_cases.append({
+            'id': f'{name}, unaligned sequential 64k',
+            'block-size': '16k',
+            'dir': path
+        })
+
+    result = simplebench.bench(auto_count_bench_func, envs,
+                               aligned_cases + unaligned_cases, count=5)
+    print(results_to_text(result))
+    with open('results.json', 'w') as f:
+        json.dump(result, f, indent=4)
-- 
2.21.3



^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 19/21] simplebench/results_to_text: add difference line to the table
  2020-10-21 14:58 ` [PATCH v7 19/21] simplebench/results_to_text: add difference line to " Vladimir Sementsov-Ogievskiy
@ 2020-10-24 10:24   ` Vladimir Sementsov-Ogievskiy
  2020-11-13 16:21   ` Max Reitz
  1 sibling, 0 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-10-24 10:24 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, eblake, armbru, fam, stefanha, mreitz, kwolf, den

21.10.2020 17:58, Vladimir Sementsov-Ogievskiy wrote:
> @@ -39,21 +43,70 @@ def result_to_text(result):
>           return 'FAILED'
>   
>   
> -def results_to_text(results):
> -    """Return text representation of bench() returned dict."""
> -    from tabulate import tabulate
> -
> +def results_dimension(results):
>       dim = None
> -    tab = [[""] + [c['id'] for c in results['envs']]]
>       for case in results['cases']:
> -        row = [case['id']]
>           for env in results['envs']:
>               res = results['tab'][case['id']][env['id']]
>               if dim is None:
>                   dim = res['dimension']
>               else:
>                   assert dim == res['dimension']
> +
> +    assert dim in ('iops', 'sec')

s/sec/seconds/

> +
> +    return dim
> +
> +


-- 
Best regards,
Vladimir


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 07/21] block: bdrv_check_perm(): process children anyway
  2020-10-21 14:58 ` [PATCH v7 07/21] block: bdrv_check_perm(): process children anyway Vladimir Sementsov-Ogievskiy
@ 2020-11-13 13:59   ` Max Reitz
  0 siblings, 0 replies; 42+ messages in thread
From: Max Reitz @ 2020-11-13 13:59 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Do generic processing even for drivers which define .bdrv_check_perm
> handler. It's needed for further preallocate filter: it will need to do
> additional action on bdrv_check_perm, but don't want to reimplement
> generic logic.
> 
> The patch doesn't change existing behaviour: the only driver that
> implements bdrv_check_perm is file-posix, but it never has any
> children.
> 
> Also, bdrv_set_perm() don't stop processing if driver has
> .bdrv_set_perm handler as well.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   block.c | 7 +++++--
>   1 file changed, 5 insertions(+), 2 deletions(-)

Reviewed-by: Max Reitz <mreitz@redhat.com>



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 08/21] block: introduce preallocate filter
  2020-10-21 14:58 ` [PATCH v7 08/21] block: introduce preallocate filter Vladimir Sementsov-Ogievskiy
@ 2020-11-13 14:32   ` Max Reitz
  2020-11-13 15:46     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 42+ messages in thread
From: Max Reitz @ 2020-11-13 14:32 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> It's intended to be inserted between format and protocol nodes to
> preallocate additional space (expanding protocol file) on writes
> crossing EOF. It improves performance for file-systems with slow
> allocation.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   docs/system/qemu-block-drivers.rst.inc |  26 ++
>   qapi/block-core.json                   |  20 +-
>   block/preallocate.c                    | 559 +++++++++++++++++++++++++
>   block/meson.build                      |   1 +
>   4 files changed, 605 insertions(+), 1 deletion(-)
>   create mode 100644 block/preallocate.c

[...]

> +    if (end <= s->file_end) {
> +        /* No preallocation needed. */
> +        return want_merge_zero && offset >= s->zero_start;
> +    }
> +
> +    /* Now we want new preallocation, as request writes beyond s->data_end. */

True, but isn’t s->file_end more important?

> +
> +    prealloc_start = want_merge_zero ? MIN(offset, s->file_end) : s->file_end;
> +    prealloc_end = QEMU_ALIGN_UP(end + s->opts.prealloc_size,
> +                                 s->opts.prealloc_align);

[...]

> +        if (prealloc == PREALLOC_MODE_FALLOC) {
> +            /*
> +             * If offset <= s->file_end, the task is already done, just
> +             * update s->file_end, to move part of "filter preallocation"

s/file_end/data_end/

With those fixed, and with %s/5\.2/6.0/:

Reviewed-by: Max Reitz <mreitz@redhat.com>

> +             * to "preallocation requested by user".
> +             * Otherwise just proceed to preallocate missing part.
> +             */
> +            if (offset <= s->file_end) {
> +                s->data_end = offset;
> +                return 0;
> +            }



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 11/21] iotests.py: execute_setup_common(): add required_fmts argument
  2020-10-21 14:58 ` [PATCH v7 11/21] iotests.py: execute_setup_common(): add required_fmts argument Vladimir Sementsov-Ogievskiy
@ 2020-11-13 14:42   ` Max Reitz
  0 siblings, 0 replies; 42+ messages in thread
From: Max Reitz @ 2020-11-13 14:42 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Add a parameter to skip test if some needed additional formats are not
> supported (for example filter drivers).
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   tests/qemu-iotests/iotests.py | 9 ++++++++-
>   1 file changed, 8 insertions(+), 1 deletion(-)

Reviewed-by: Max Reitz <mreitz@redhat.com>



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 12/21] iotests: add 298 to test new preallocate filter driver
  2020-10-21 14:58 ` [PATCH v7 12/21] iotests: add 298 to test new preallocate filter driver Vladimir Sementsov-Ogievskiy
@ 2020-11-13 14:43   ` Max Reitz
  0 siblings, 0 replies; 42+ messages in thread
From: Max Reitz @ 2020-11-13 14:43 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   tests/qemu-iotests/298     | 186 +++++++++++++++++++++++++++++++++++++
>   tests/qemu-iotests/298.out |   5 +
>   tests/qemu-iotests/group   |   1 +
>   3 files changed, 192 insertions(+)
>   create mode 100644 tests/qemu-iotests/298
>   create mode 100644 tests/qemu-iotests/298.out

Reviewed-by: Max Reitz <mreitz@redhat.com>



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 13/21] scripts/simplebench: fix grammar: s/successed/succeeded/
  2020-10-21 14:58 ` [PATCH v7 13/21] scripts/simplebench: fix grammar: s/successed/succeeded/ Vladimir Sementsov-Ogievskiy
@ 2020-11-13 14:44   ` Max Reitz
  0 siblings, 0 replies; 42+ messages in thread
From: Max Reitz @ 2020-11-13 14:44 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   scripts/simplebench/simplebench.py | 12 ++++++------
>   1 file changed, 6 insertions(+), 6 deletions(-)

Reviewed-by: Max Reitz <mreitz@redhat.com>



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 14/21] scripts/simplebench: support iops
  2020-10-21 14:58 ` [PATCH v7 14/21] scripts/simplebench: support iops Vladimir Sementsov-Ogievskiy
@ 2020-11-13 15:02   ` Max Reitz
  0 siblings, 0 replies; 42+ messages in thread
From: Max Reitz @ 2020-11-13 15:02 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Support benchmarks returning not seconds but iops. We'll use it for
> further new test.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   scripts/simplebench/simplebench.py | 38 ++++++++++++++++++++++--------
>   1 file changed, 28 insertions(+), 10 deletions(-)

Reviewed-by: Max Reitz <mreitz@redhat.com>



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 15/21] scripts/simplebench: use standard deviation for +- error
  2020-10-21 14:58 ` [PATCH v7 15/21] scripts/simplebench: use standard deviation for +- error Vladimir Sementsov-Ogievskiy
@ 2020-11-13 15:30   ` Max Reitz
  0 siblings, 0 replies; 42+ messages in thread
From: Max Reitz @ 2020-11-13 15:30 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Standard deviation is more usual to see after +- than current maximum
> of deviations.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   scripts/simplebench/simplebench.py | 11 ++++++-----
>   1 file changed, 6 insertions(+), 5 deletions(-)

Reviewed-by: Max Reitz <mreitz@redhat.com>



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 16/21] simplebench: rename ascii() to results_to_text()
  2020-10-21 14:58 ` [PATCH v7 16/21] simplebench: rename ascii() to results_to_text() Vladimir Sementsov-Ogievskiy
@ 2020-11-13 15:31   ` Max Reitz
  0 siblings, 0 replies; 42+ messages in thread
From: Max Reitz @ 2020-11-13 15:31 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Next patch will use utf8 plus-minus symbol, let's use more generic (and
> more readable) name.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   scripts/simplebench/bench-example.py   |  2 +-
>   scripts/simplebench/bench_write_req.py |  2 +-
>   scripts/simplebench/simplebench.py     | 10 +++++-----
>   3 files changed, 7 insertions(+), 7 deletions(-)

Reviewed-by: Max Reitz <mreitz@redhat.com>



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 17/21] simplebench: move results_to_text() into separate file
  2020-10-21 14:58 ` [PATCH v7 17/21] simplebench: move results_to_text() into separate file Vladimir Sementsov-Ogievskiy
@ 2020-11-13 15:39   ` Max Reitz
  0 siblings, 0 replies; 42+ messages in thread
From: Max Reitz @ 2020-11-13 15:39 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Let's keep view part in separate: this way it's better to improve it in
> the following commits.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   scripts/simplebench/bench-example.py   |  3 +-
>   scripts/simplebench/bench_write_req.py |  3 +-
>   scripts/simplebench/results_to_text.py | 48 ++++++++++++++++++++++++++
>   scripts/simplebench/simplebench.py     | 31 -----------------
>   4 files changed, 52 insertions(+), 33 deletions(-)
>   create mode 100644 scripts/simplebench/results_to_text.py

Reviewed-by: Max Reitz <mreitz@redhat.com>



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 08/21] block: introduce preallocate filter
  2020-11-13 14:32   ` Max Reitz
@ 2020-11-13 15:46     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 0 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-11-13 15:46 UTC (permalink / raw)
  To: Max Reitz, qemu-block
  Cc: qemu-devel, eblake, armbru, fam, stefanha, kwolf, den

13.11.2020 17:32, Max Reitz wrote:
> On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
>> It's intended to be inserted between format and protocol nodes to
>> preallocate additional space (expanding protocol file) on writes
>> crossing EOF. It improves performance for file-systems with slow
>> allocation.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> ---
>>   docs/system/qemu-block-drivers.rst.inc |  26 ++
>>   qapi/block-core.json                   |  20 +-
>>   block/preallocate.c                    | 559 +++++++++++++++++++++++++
>>   block/meson.build                      |   1 +
>>   4 files changed, 605 insertions(+), 1 deletion(-)
>>   create mode 100644 block/preallocate.c
> 
> [...]
> 
>> +    if (end <= s->file_end) {
>> +        /* No preallocation needed. */
>> +        return want_merge_zero && offset >= s->zero_start;
>> +    }
>> +
>> +    /* Now we want new preallocation, as request writes beyond s->data_end. */
> 
> True, but isn’t s->file_end more important?

Yes, file_end should be here.

> 
>> +
>> +    prealloc_start = want_merge_zero ? MIN(offset, s->file_end) : s->file_end;
>> +    prealloc_end = QEMU_ALIGN_UP(end + s->opts.prealloc_size,
>> +                                 s->opts.prealloc_align);
> 
> [...]
> 
>> +        if (prealloc == PREALLOC_MODE_FALLOC) {
>> +            /*
>> +             * If offset <= s->file_end, the task is already done, just
>> +             * update s->file_end, to move part of "filter preallocation"
> 
> s/file_end/data_end/

yes

> 
> With those fixed, and with %s/5\.2/6.0/:
> 
> Reviewed-by: Max Reitz <mreitz@redhat.com>

Thanks!


-- 
Best regards,
Vladimir


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 18/21] simplebench/results_to_text: improve view of the table
  2020-10-21 14:58 ` [PATCH v7 18/21] simplebench/results_to_text: improve view of the table Vladimir Sementsov-Ogievskiy
@ 2020-11-13 15:59   ` Max Reitz
  2020-11-13 16:24     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 42+ messages in thread
From: Max Reitz @ 2020-11-13 15:59 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Move to generic format for floats and percentage for error.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   scripts/simplebench/results_to_text.py | 13 ++++++++++++-
>   1 file changed, 12 insertions(+), 1 deletion(-)
> 
> diff --git a/scripts/simplebench/results_to_text.py b/scripts/simplebench/results_to_text.py
> index 58d909ffd9..479f7ac1d4 100644
> --- a/scripts/simplebench/results_to_text.py
> +++ b/scripts/simplebench/results_to_text.py
> @@ -16,11 +16,22 @@
>   # along with this program.  If not, see <http://www.gnu.org/licenses/>.
>   #
>   
> +import math
> +
> +
> +def format_value(x, stdev):
> +    stdev_pr = stdev / x * 100
> +    if stdev_pr < 1.5:
> +        # don't care too much
> +        return f'{x:.2g}'
> +    else:
> +        return f'{x:.2g} ± {math.ceil(stdev_pr)}%'

OK, so no magnitude-based precision this time (except for the %f -> %g 
change).  Works for me.

Other than that, I personally don’t like the relative standard deviation 
much, because the absolute SD immediately shows the 68 % boundaries (and 
an idea on the 95 % boundaries with 2σ), whereas the RSD just gives an 
impression on how spread out the data is.  (Which I find the absolute SD 
also does, when given together with the average, which is the case here.)

To be completely honest, though, I didn’t even know the term “relative 
SD” existed until a couple of minutes ago, and I didn’t know it was 
something that was used at all.
And if I haven’t seen the RSD used in practice, I can’t confidently say 
that I have good reasons not to like it.

But, well, I can’t have any confidence in liking it either, and because 
the change from ASD to RSD is basically the most important change of 
this patch (which I can’t really agree is an improvement), I can’t 
really give an R-b.

Perhaps this is OK:

Acked-by: Max Reitz <mreitz@redhat.com>

>   
>   def result_to_text(result):
>       """Return text representation of bench_one() returned dict."""
>       if 'average' in result:
> -        s = '{:.2f} +- {:.2f}'.format(result['average'], result['stdev'])
> +        s = format_value(result['average'], result['stdev'])
>           if 'n-failed' in result:
>               s += '\n({} failed)'.format(result['n-failed'])
>           return s
> 



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 19/21] simplebench/results_to_text: add difference line to the table
  2020-10-21 14:58 ` [PATCH v7 19/21] simplebench/results_to_text: add difference line to " Vladimir Sementsov-Ogievskiy
  2020-10-24 10:24   ` Vladimir Sementsov-Ogievskiy
@ 2020-11-13 16:21   ` Max Reitz
  1 sibling, 0 replies; 42+ messages in thread
From: Max Reitz @ 2020-11-13 16:21 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Performance improvements / degradations are usually discussed in
> percentage. Let's make the script calculate it for us.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   scripts/simplebench/results_to_text.py | 67 +++++++++++++++++++++++---
>   1 file changed, 60 insertions(+), 7 deletions(-)
> 
> diff --git a/scripts/simplebench/results_to_text.py b/scripts/simplebench/results_to_text.py
> index 479f7ac1d4..56fdacf7ca 100644
> --- a/scripts/simplebench/results_to_text.py
> +++ b/scripts/simplebench/results_to_text.py

[...]

> +            for j in range(0, i):
> +                env_j = results['envs'][j]
> +                res_j = case_results[env_j['id']]
> +                cell += ' '
> +
> +                if 'average' not in res_j:
> +                    # Failed result
> +                    cell += '--'
> +                    continue
> +
> +                col_j = tab[0][j + 1] if named_columns else ''
> +                diff_pr = round((res['average'] - res_j['average']) /
> +                                res_j['average'] * 100)
> +                cell += f' {col_j}{diff_pr:+}%'

Contrasting to v6, you added the "cell += ' '" line, dropped a space in 
the "cell += '--'" line (was: "cell += ' --'"), but kept the space here. 
  I would have assumed that the leading space is dropped here, too.  But 
I don’t quite know, what I should be expecting, so.

Anyway, I’ll just leave this here:

Reviewed-by: Max Reitz <mreitz@redhat.com>

> +            row.append(cell)
> +        tab.append(row)
> +
> +    return f'All results are in {dim}\n\n' + tabulate.tabulate(tab)
> 



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 20/21] simplebench/results_to_text: make executable
  2020-10-21 14:58 ` [PATCH v7 20/21] simplebench/results_to_text: make executable Vladimir Sementsov-Ogievskiy
@ 2020-11-13 16:22   ` Max Reitz
  0 siblings, 0 replies; 42+ messages in thread
From: Max Reitz @ 2020-11-13 16:22 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Make results_to_text a tool to dump results saved in JSON file.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   scripts/simplebench/results_to_text.py | 14 ++++++++++++++
>   1 file changed, 14 insertions(+)
>   mode change 100644 => 100755 scripts/simplebench/results_to_text.py

Reviewed-by: Max Reitz <mreitz@redhat.com>



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 18/21] simplebench/results_to_text: improve view of the table
  2020-11-13 15:59   ` Max Reitz
@ 2020-11-13 16:24     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 0 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-11-13 16:24 UTC (permalink / raw)
  To: Max Reitz, qemu-block
  Cc: qemu-devel, eblake, armbru, fam, stefanha, kwolf, den

13.11.2020 18:59, Max Reitz wrote:
> On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
>> Move to generic format for floats and percentage for error.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> ---
>>   scripts/simplebench/results_to_text.py | 13 ++++++++++++-
>>   1 file changed, 12 insertions(+), 1 deletion(-)
>>
>> diff --git a/scripts/simplebench/results_to_text.py b/scripts/simplebench/results_to_text.py
>> index 58d909ffd9..479f7ac1d4 100644
>> --- a/scripts/simplebench/results_to_text.py
>> +++ b/scripts/simplebench/results_to_text.py
>> @@ -16,11 +16,22 @@
>>   # along with this program.  If not, see <http://www.gnu.org/licenses/>.
>>   #
>> +import math
>> +
>> +
>> +def format_value(x, stdev):
>> +    stdev_pr = stdev / x * 100
>> +    if stdev_pr < 1.5:
>> +        # don't care too much
>> +        return f'{x:.2g}'
>> +    else:
>> +        return f'{x:.2g} ± {math.ceil(stdev_pr)}%'
> 
> OK, so no magnitude-based precision this time (except for the %f -> %g change).  Works for me.
> 
> Other than that, I personally don’t like the relative standard deviation much, because the absolute SD immediately shows the 68 % boundaries (and an idea on the 95 % boundaries with 2σ), whereas the RSD just gives an impression on how spread out the data is.  (Which I find the absolute SD also does, when given together with the average, which is the case here.)

I realized that for myself, the only thing I need from +-deviation is a kind of "reliability" of the result. And it's more obvious for me in percentage. Looking at the table with a lot of numbers, where most of values have deviation less than +-5%, some values with deviation +-30 would catch the eye immediately. And with absolute SD I have to look through the table more carefully.

> 
> To be completely honest, though, I didn’t even know the term “relative SD” existed until a couple of minutes ago, and I didn’t know it was something that was used at all.
> And if I haven’t seen the RSD used in practice, I can’t confidently say that I have good reasons not to like it.
> 
> But, well, I can’t have any confidence in liking it either, and because the change from ASD to RSD is basically the most important change of this patch (which I can’t really agree is an improvement), I can’t really give an R-b.
> 
> Perhaps this is OK:
> 
> Acked-by: Max Reitz <mreitz@redhat.com>
> 

Thanks!

I don't have strict opinion about how this should look. For now these scripts don't have wide usage... We can always add an option to adjust table view, and I will not mind making absolute SD the default view.


-- 
Best regards,
Vladimir


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 21/21] scripts/simplebench: add bench_prealloc.py
  2020-10-21 14:58 ` [PATCH v7 21/21] scripts/simplebench: add bench_prealloc.py Vladimir Sementsov-Ogievskiy
@ 2020-11-13 16:24   ` Max Reitz
  2020-11-13 18:00     ` Vladimir Sementsov-Ogievskiy
  0 siblings, 1 reply; 42+ messages in thread
From: Max Reitz @ 2020-11-13 16:24 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Benchmark for new preallocate filter.
> 
> Example usage:
>      ./bench_prealloc.py ../../build/qemu-img \
>          ssd-ext4:/path/to/mount/point \
>          ssd-xfs:/path2 hdd-ext4:/path3 hdd-xfs:/path4
> 
> The benchmark shows performance improvement (or degradation) when use
> new preallocate filter with qcow2 image.
> 
> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
> ---
>   scripts/simplebench/bench_prealloc.py | 132 ++++++++++++++++++++++++++
>   1 file changed, 132 insertions(+)
>   create mode 100755 scripts/simplebench/bench_prealloc.py

Reviewed-by: Max Reitz <mreitz@redhat.com>



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 21/21] scripts/simplebench: add bench_prealloc.py
  2020-11-13 16:24   ` Max Reitz
@ 2020-11-13 18:00     ` Vladimir Sementsov-Ogievskiy
  2020-11-13 19:32       ` Max Reitz
  0 siblings, 1 reply; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-11-13 18:00 UTC (permalink / raw)
  To: Max Reitz, qemu-block
  Cc: qemu-devel, eblake, armbru, fam, stefanha, kwolf, den

13.11.2020 19:24, Max Reitz wrote:
> On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
>> Benchmark for new preallocate filter.
>>
>> Example usage:
>>      ./bench_prealloc.py ../../build/qemu-img \
>>          ssd-ext4:/path/to/mount/point \
>>          ssd-xfs:/path2 hdd-ext4:/path3 hdd-xfs:/path4
>>
>> The benchmark shows performance improvement (or degradation) when use
>> new preallocate filter with qcow2 image.
>>
>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>> ---
>>   scripts/simplebench/bench_prealloc.py | 132 ++++++++++++++++++++++++++
>>   1 file changed, 132 insertions(+)
>>   create mode 100755 scripts/simplebench/bench_prealloc.py
> 
> Reviewed-by: Max Reitz <mreitz@redhat.com>
> 


Thanks for reviewing! Could you take this all through you tree?

-- 
Best regards,
Vladimir


^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 21/21] scripts/simplebench: add bench_prealloc.py
  2020-11-13 18:00     ` Vladimir Sementsov-Ogievskiy
@ 2020-11-13 19:32       ` Max Reitz
  0 siblings, 0 replies; 42+ messages in thread
From: Max Reitz @ 2020-11-13 19:32 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 13.11.20 19:00, Vladimir Sementsov-Ogievskiy wrote:
> 13.11.2020 19:24, Max Reitz wrote:
>> On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
>>> Benchmark for new preallocate filter.
>>>
>>> Example usage:
>>>      ./bench_prealloc.py ../../build/qemu-img \
>>>          ssd-ext4:/path/to/mount/point \
>>>          ssd-xfs:/path2 hdd-ext4:/path3 hdd-xfs:/path4
>>>
>>> The benchmark shows performance improvement (or degradation) when use
>>> new preallocate filter with qcow2 image.
>>>
>>> Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
>>> ---
>>>   scripts/simplebench/bench_prealloc.py | 132 ++++++++++++++++++++++++++
>>>   1 file changed, 132 insertions(+)
>>>   create mode 100755 scripts/simplebench/bench_prealloc.py
>>
>> Reviewed-by: Max Reitz <mreitz@redhat.com>
>>
> 
> 
> Thanks for reviewing! Could you take this all through you tree?

Sure, if you can live with the fact that I’ll be on PTO for the next two 
weeks :)

(Shouldn’t matter for block-next, as 5.2 won’t be released until December.)

Max



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 00/21] preallocate filter
  2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
                   ` (20 preceding siblings ...)
  2020-10-21 14:58 ` [PATCH v7 21/21] scripts/simplebench: add bench_prealloc.py Vladimir Sementsov-Ogievskiy
@ 2020-11-13 19:33 ` Max Reitz
  2020-11-16  9:32   ` Vladimir Sementsov-Ogievskiy
  21 siblings, 1 reply; 42+ messages in thread
From: Max Reitz @ 2020-11-13 19:33 UTC (permalink / raw)
  To: Vladimir Sementsov-Ogievskiy, qemu-block
  Cc: fam, kwolf, den, qemu-devel, armbru, stefanha

On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
> Hi all!
> 
> Here is a filter, which does preallocation on write.
> 
> v7:
> 01: add Alberto's r-b
> 07: don't remove sentence from the comment
> 08: - drop extra "s->file_end = end;" line
>      - improve check/set perm handlers
> 09: add Max's r-b
> 10: add Max's r-b
> 11: new
> 12: - skip if preallocate unsupported
>      - drop auto and quick groups
> 13: new
> 14: - improve 'average' field of result spec
>      - drop extra 'dim = ...' line
> 15-18: new
> 19: a lot of changes
> 20: new
> 21: add results dump to json

Thanks, applied to my block-next branch (for 6.0):

https://git.xanclic.moe/XanClic/qemu/commits/branch/block-next

Max



^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v7 00/21] preallocate filter
  2020-11-13 19:33 ` [PATCH v7 00/21] preallocate filter Max Reitz
@ 2020-11-16  9:32   ` Vladimir Sementsov-Ogievskiy
  0 siblings, 0 replies; 42+ messages in thread
From: Vladimir Sementsov-Ogievskiy @ 2020-11-16  9:32 UTC (permalink / raw)
  To: Max Reitz, qemu-block
  Cc: qemu-devel, eblake, armbru, fam, stefanha, kwolf, den

13.11.2020 22:33, Max Reitz wrote:
> On 21.10.20 16:58, Vladimir Sementsov-Ogievskiy wrote:
>> Hi all!
>>
>> Here is a filter, which does preallocation on write.
>>
>> v7:
>> 01: add Alberto's r-b
>> 07: don't remove sentence from the comment
>> 08: - drop extra "s->file_end = end;" line
>>      - improve check/set perm handlers
>> 09: add Max's r-b
>> 10: add Max's r-b
>> 11: new
>> 12: - skip if preallocate unsupported
>>      - drop auto and quick groups
>> 13: new
>> 14: - improve 'average' field of result spec
>>      - drop extra 'dim = ...' line
>> 15-18: new
>> 19: a lot of changes
>> 20: new
>> 21: add results dump to json
> 
> Thanks, applied to my block-next branch (for 6.0):
> 
> https://git.xanclic.moe/XanClic/qemu/commits/branch/block-next
> 

Thank you!


-- 
Best regards,
Vladimir


^ permalink raw reply	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2020-11-16  9:39 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-21 14:58 [PATCH v7 00/21] preallocate filter Vladimir Sementsov-Ogievskiy
2020-10-21 14:58 ` [PATCH v7 01/21] block: simplify comment to BDRV_REQ_SERIALISING Vladimir Sementsov-Ogievskiy
2020-10-21 14:58 ` [PATCH v7 02/21] block/io.c: drop assertion on double waiting for request serialisation Vladimir Sementsov-Ogievskiy
2020-10-21 14:58 ` [PATCH v7 03/21] block/io: split out bdrv_find_conflicting_request Vladimir Sementsov-Ogievskiy
2020-10-21 14:58 ` [PATCH v7 04/21] block/io: bdrv_wait_serialising_requests_locked: drop extra bs arg Vladimir Sementsov-Ogievskiy
2020-10-21 14:58 ` [PATCH v7 05/21] block: bdrv_mark_request_serialising: split non-waiting function Vladimir Sementsov-Ogievskiy
2020-10-21 14:58 ` [PATCH v7 06/21] block: introduce BDRV_REQ_NO_WAIT flag Vladimir Sementsov-Ogievskiy
2020-10-21 14:58 ` [PATCH v7 07/21] block: bdrv_check_perm(): process children anyway Vladimir Sementsov-Ogievskiy
2020-11-13 13:59   ` Max Reitz
2020-10-21 14:58 ` [PATCH v7 08/21] block: introduce preallocate filter Vladimir Sementsov-Ogievskiy
2020-11-13 14:32   ` Max Reitz
2020-11-13 15:46     ` Vladimir Sementsov-Ogievskiy
2020-10-21 14:58 ` [PATCH v7 09/21] qemu-io: add preallocate mode parameter for truncate command Vladimir Sementsov-Ogievskiy
2020-10-21 14:58 ` [PATCH v7 10/21] iotests: qemu_io_silent: support --image-opts Vladimir Sementsov-Ogievskiy
2020-10-21 14:58 ` [PATCH v7 11/21] iotests.py: execute_setup_common(): add required_fmts argument Vladimir Sementsov-Ogievskiy
2020-11-13 14:42   ` Max Reitz
2020-10-21 14:58 ` [PATCH v7 12/21] iotests: add 298 to test new preallocate filter driver Vladimir Sementsov-Ogievskiy
2020-11-13 14:43   ` Max Reitz
2020-10-21 14:58 ` [PATCH v7 13/21] scripts/simplebench: fix grammar: s/successed/succeeded/ Vladimir Sementsov-Ogievskiy
2020-11-13 14:44   ` Max Reitz
2020-10-21 14:58 ` [PATCH v7 14/21] scripts/simplebench: support iops Vladimir Sementsov-Ogievskiy
2020-11-13 15:02   ` Max Reitz
2020-10-21 14:58 ` [PATCH v7 15/21] scripts/simplebench: use standard deviation for +- error Vladimir Sementsov-Ogievskiy
2020-11-13 15:30   ` Max Reitz
2020-10-21 14:58 ` [PATCH v7 16/21] simplebench: rename ascii() to results_to_text() Vladimir Sementsov-Ogievskiy
2020-11-13 15:31   ` Max Reitz
2020-10-21 14:58 ` [PATCH v7 17/21] simplebench: move results_to_text() into separate file Vladimir Sementsov-Ogievskiy
2020-11-13 15:39   ` Max Reitz
2020-10-21 14:58 ` [PATCH v7 18/21] simplebench/results_to_text: improve view of the table Vladimir Sementsov-Ogievskiy
2020-11-13 15:59   ` Max Reitz
2020-11-13 16:24     ` Vladimir Sementsov-Ogievskiy
2020-10-21 14:58 ` [PATCH v7 19/21] simplebench/results_to_text: add difference line to " Vladimir Sementsov-Ogievskiy
2020-10-24 10:24   ` Vladimir Sementsov-Ogievskiy
2020-11-13 16:21   ` Max Reitz
2020-10-21 14:58 ` [PATCH v7 20/21] simplebench/results_to_text: make executable Vladimir Sementsov-Ogievskiy
2020-11-13 16:22   ` Max Reitz
2020-10-21 14:58 ` [PATCH v7 21/21] scripts/simplebench: add bench_prealloc.py Vladimir Sementsov-Ogievskiy
2020-11-13 16:24   ` Max Reitz
2020-11-13 18:00     ` Vladimir Sementsov-Ogievskiy
2020-11-13 19:32       ` Max Reitz
2020-11-13 19:33 ` [PATCH v7 00/21] preallocate filter Max Reitz
2020-11-16  9:32   ` Vladimir Sementsov-Ogievskiy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).