All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate
@ 2017-05-26 16:55 Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 01/16] block: Add PreallocMode to BD.bdrv_truncate() Max Reitz
                   ` (15 more replies)
  0 siblings, 16 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

=== Series dependencies ===

This series depends on v6 of Stefan's series
"qemu-img: add measure sub-command"
(http://lists.nongnu.org/archive/html/qemu-devel/2017-05/msg01706.html).


=== Actual cover letter ===

This series adds preallocation to bdrv_truncate() and subsequently
qemu-img resize. This is implemented for qcow2 and raw only, just like
preallocation for newly created images is. There is no runtime interface
for this new parameter (yet).


=== v3 ===

General overview: Rebased on Stefan's series, addressed comments from
v2. The most important change is that I rethought the whole concept of
how to make sure that we actually have allocated the right amount of
space in a qcow2 file.

What we really want is to just preallocate the new data area. All
metadata we'll have to create along the way will be allocated
automatically, so there is no need to preallocate that as well. But we
need to preallocate the data, and in order to be able to do by issuing a
bdrv_truncate() on the underlying file, we need to place it at the end
of the qcow2 file (therefore we cannot really preallocate the data in
preallocate(): The allocation function used there may place the data
anywhere, and it may even put refcount structures afterwards if you're
really unlucky).

So we need some way to allocate a big chunk of data in a qcow2 file
without being interrupted by refcount structure allocations or anything.
It'd also be nice not to have to guess where the refcount table is
placed (if it is resized) or how many refcount blocks we'll need or
anything like that.
We can do all of that by allocating the necessary refcount structures
beforehand. Our alloc_refcount_block() function does perform this
functionality when growing the refcount table. In order to make this
usable, we'll have to place it into an own function and allow it to
cover more clusters in addition to just the new refcount structures
themselves. This is done by patch 12.

Then we just invoke that function, truncate the underlying file to
preallocate what is supposed to be the area for the new data, increase
its refcount and hook it up in the L2 tables (patch 14).


Alternatively, we could extend the preallocate() function to preallocate
the data in the underlying file. But this would require a
bdrv_preallocate() function because the new data may be placed anywhere
and bdrv_truncate() can only be used to preallocate data at the end of a
file. All in all, this would probably result in a series with a very
similar diff stat. Since I put all of my work into this version here, I
elect not to try it (unless someone thinks it would be useful) -- to be
honest, I just got the idea while writing this cover letter.
(And I personally like qcow2_refcount_area() more than what
 alloc_refcount_block() did before, although it is considerably longer.
 But maybe that is just a personal bias because I wrote it myself.)


Short per-patch differences:
- Patch 4: Rebase conflict (bumped OPTION_PREALLOCATION from 264 to 265)
- Patch 9: Use uint64_t instead of int64_t [Philippe]
- Patch 10: We should not call a *_co_* function unless in a coroutine;
            and we don't need to lock the mutex outside of a coroutine.
- Patches 12/13: New (old patches 12/13 have been dropped)
- Patch 14: Completely new implementation


git-backport-diff against v2:

Key:
[----] : patches are identical
[####] : number of functional differences between upstream/downstream patch
[down] : patch is downstream-only
The flags [FC] indicate (F)unctional and (C)ontextual differences, respectively

001/16:[----] [-C] 'block: Add PreallocMode to BD.bdrv_truncate()'
002/16:[----] [-C] 'block: Add PreallocMode to bdrv_truncate()'
003/16:[----] [-C] 'block: Add PreallocMode to blk_truncate()'
004/16:[0002] [FC] 'qemu-img: Expose PreallocMode for resizing'
005/16:[----] [--] 'block/file-posix: Small fixes in raw_create()'
006/16:[----] [--] 'block/file-posix: Extract raw_regular_truncate()'
007/16:[----] [--] 'block/file-posix: Generalize raw_regular_truncate'
008/16:[----] [--] 'block/file-posix: Preallocation for truncate'
009/16:[0003] [FC] 'block/qcow2: Generalize preallocate()'
010/16:[0008] [FC] 'block/qcow2: Lock s->lock in preallocate()'
011/16:[----] [--] 'block/qcow2: Metadata preallocation for truncate'
012/16:[down] 'block/qcow2: Add qcow2_refcount_area()'
013/16:[down] 'block/qcow2: Rename "fail_block" to just "fail"'
014/16:[0117] [FC] 'block/qcow2: falloc/full preallocating growth'
015/16:[----] [--] 'iotests: Add preallocated resize test for raw'
016/16:[----] [--] 'iotests: Add preallocated growth test for qcow2'


Max Reitz (16):
  block: Add PreallocMode to BD.bdrv_truncate()
  block: Add PreallocMode to bdrv_truncate()
  block: Add PreallocMode to blk_truncate()
  qemu-img: Expose PreallocMode for resizing
  block/file-posix: Small fixes in raw_create()
  block/file-posix: Extract raw_regular_truncate()
  block/file-posix: Generalize raw_regular_truncate
  block/file-posix: Preallocation for truncate
  block/qcow2: Generalize preallocate()
  block/qcow2: Lock s->lock in preallocate()
  block/qcow2: Metadata preallocation for truncate
  block/qcow2: Add qcow2_refcount_area()
  block/qcow2: Rename "fail_block" to just "fail"
  block/qcow2: falloc/full preallocating growth
  iotests: Add preallocated resize test for raw
  iotests: Add preallocated growth test for qcow2

 block/qcow2.h                  |   9 +
 include/block/block.h          |   3 +-
 include/block/block_int.h      |   3 +-
 include/sysemu/block-backend.h |   3 +-
 block.c                        |   5 +-
 block/blkdebug.c               |   5 +-
 block/block-backend.c          |   5 +-
 block/commit.c                 |   4 +-
 block/crypto.c                 |   4 +-
 block/file-posix.c             | 201 +++++++++++++--------
 block/file-win32.c             |   9 +-
 block/gluster.c                |   8 +-
 block/iscsi.c                  |   9 +-
 block/mirror.c                 |   3 +-
 block/nfs.c                    |   9 +-
 block/parallels.c              |  13 +-
 block/qcow.c                   |   8 +-
 block/qcow2-refcount.c         | 277 +++++++++++++++++++----------
 block/qcow2.c                  | 196 ++++++++++++++++++---
 block/qed.c                    |  11 +-
 block/raw-format.c             |   5 +-
 block/rbd.c                    |   9 +-
 block/sheepdog.c               |  11 +-
 block/vdi.c                    |   3 +-
 block/vhdx-log.c               |   2 +-
 block/vhdx.c                   |   8 +-
 block/vmdk.c                   |   7 +-
 block/vpc.c                    |   2 +-
 blockdev.c                     |   2 +-
 qemu-img.c                     |  33 +++-
 qemu-io-cmds.c                 |   2 +-
 qemu-img.texi                  |   7 +-
 tests/qemu-iotests/044.out     |   2 +-
 tests/qemu-iotests/106         |  92 ++++++++++
 tests/qemu-iotests/106.out     |  50 ++++++
 tests/qemu-iotests/125         | 130 ++++++++++++++
 tests/qemu-iotests/125.out     | 386 +++++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/group       |   2 +
 38 files changed, 1304 insertions(+), 234 deletions(-)
 create mode 100755 tests/qemu-iotests/106
 create mode 100644 tests/qemu-iotests/106.out
 create mode 100755 tests/qemu-iotests/125
 create mode 100644 tests/qemu-iotests/125.out

-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 01/16] block: Add PreallocMode to BD.bdrv_truncate()
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 02/16] block: Add PreallocMode to bdrv_truncate() Max Reitz
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

Add a PreallocMode parameter to the bdrv_truncate() function implemented
by each block driver. Currently, we always pass PREALLOC_MODE_OFF and no
driver accepts anything else.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 include/block/block_int.h |  3 ++-
 block.c                   |  2 +-
 block/blkdebug.c          |  9 ++++++++-
 block/crypto.c            |  8 +++++++-
 block/file-posix.c        |  9 ++++++++-
 block/file-win32.c        |  9 ++++++++-
 block/gluster.c           |  8 +++++++-
 block/iscsi.c             |  9 ++++++++-
 block/nfs.c               |  9 ++++++++-
 block/qcow2.c             |  9 ++++++++-
 block/qed.c               |  9 ++++++++-
 block/raw-format.c        |  9 ++++++++-
 block/rbd.c               |  9 ++++++++-
 block/sheepdog.c          | 11 +++++++++--
 14 files changed, 98 insertions(+), 15 deletions(-)

diff --git a/include/block/block_int.h b/include/block/block_int.h
index 8a423c9..d6cdbd8 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -203,7 +203,8 @@ struct BlockDriver {
     int coroutine_fn (*bdrv_co_flush_to_os)(BlockDriverState *bs);
 
     const char *protocol_name;
-    int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset, Error **errp);
+    int (*bdrv_truncate)(BlockDriverState *bs, int64_t offset,
+                         PreallocMode prealloc, Error **errp);
 
     int64_t (*bdrv_getlength)(BlockDriverState *bs);
     bool has_variable_length;
diff --git a/block.c b/block.c
index 67a8bfc..0d8fe01 100644
--- a/block.c
+++ b/block.c
@@ -3377,7 +3377,7 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, Error **errp)
 
     assert(!(bs->open_flags & BDRV_O_INACTIVE));
 
-    ret = drv->bdrv_truncate(bs, offset, errp);
+    ret = drv->bdrv_truncate(bs, offset, PREALLOC_MODE_OFF, errp);
     if (ret == 0) {
         ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
         bdrv_dirty_bitmap_truncate(bs);
diff --git a/block/blkdebug.c b/block/blkdebug.c
index a5196e8..00090aa 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -812,8 +812,15 @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
     return bdrv_getlength(bs->file->bs);
 }
 
-static int blkdebug_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
+static int blkdebug_truncate(BlockDriverState *bs, int64_t offset,
+                             PreallocMode prealloc, Error **errp)
 {
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
     return bdrv_truncate(bs->file, offset, errp);
 }
 
diff --git a/block/crypto.c b/block/crypto.c
index 10e5ddc..7047cb2 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -382,12 +382,18 @@ static int block_crypto_create_generic(QCryptoBlockFormat format,
 }
 
 static int block_crypto_truncate(BlockDriverState *bs, int64_t offset,
-                                 Error **errp)
+                                 PreallocMode prealloc, Error **errp)
 {
     BlockCrypto *crypto = bs->opaque;
     size_t payload_offset =
         qcrypto_block_get_payload_offset(crypto->block);
 
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
     offset += payload_offset;
 
     return bdrv_truncate(bs->file, offset, errp);
diff --git a/block/file-posix.c b/block/file-posix.c
index 4354d49..7cc6bf6 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -1629,12 +1629,19 @@ static void raw_close(BlockDriverState *bs)
     }
 }
 
-static int raw_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
+static int raw_truncate(BlockDriverState *bs, int64_t offset,
+                        PreallocMode prealloc, Error **errp)
 {
     BDRVRawState *s = bs->opaque;
     struct stat st;
     int ret;
 
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
     if (fstat(s->fd, &st)) {
         ret = -errno;
         error_setg_errno(errp, -ret, "Failed to fstat() the file");
diff --git a/block/file-win32.c b/block/file-win32.c
index 8f14f0b..707fb53 100644
--- a/block/file-win32.c
+++ b/block/file-win32.c
@@ -466,12 +466,19 @@ static void raw_close(BlockDriverState *bs)
     }
 }
 
-static int raw_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
+static int raw_truncate(BlockDriverState *bs, int64_t offset,
+                        PreallocMode prealloc, Error **errp)
 {
     BDRVRawState *s = bs->opaque;
     LONG low, high;
     DWORD dwPtrLow;
 
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
     low = offset;
     high = offset >> 32;
 
diff --git a/block/gluster.c b/block/gluster.c
index 7c76cd0..e673d6c 100644
--- a/block/gluster.c
+++ b/block/gluster.c
@@ -1093,11 +1093,17 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs,
 }
 
 static int qemu_gluster_truncate(BlockDriverState *bs, int64_t offset,
-                                 Error **errp)
+                                 PreallocMode prealloc, Error **errp)
 {
     int ret;
     BDRVGlusterState *s = bs->opaque;
 
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
     ret = glfs_ftruncate(s->fd, offset);
     if (ret < 0) {
         ret = -errno;
diff --git a/block/iscsi.c b/block/iscsi.c
index 5daa201..61d5792 100644
--- a/block/iscsi.c
+++ b/block/iscsi.c
@@ -2059,11 +2059,18 @@ static void iscsi_reopen_commit(BDRVReopenState *reopen_state)
     }
 }
 
-static int iscsi_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
+static int iscsi_truncate(BlockDriverState *bs, int64_t offset,
+                          PreallocMode prealloc, Error **errp)
 {
     IscsiLun *iscsilun = bs->opaque;
     Error *local_err = NULL;
 
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
     if (iscsilun->type != TYPE_DISK) {
         error_setg(errp, "Cannot resize non-disk iSCSI devices");
         return -ENOTSUP;
diff --git a/block/nfs.c b/block/nfs.c
index 848b2c0..be6073a 100644
--- a/block/nfs.c
+++ b/block/nfs.c
@@ -758,11 +758,18 @@ static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
     return (task.ret < 0 ? task.ret : st.st_blocks * 512);
 }
 
-static int nfs_file_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
+static int nfs_file_truncate(BlockDriverState *bs, int64_t offset,
+                             PreallocMode prealloc, Error **errp)
 {
     NFSClient *client = bs->opaque;
     int ret;
 
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
     ret = nfs_ftruncate(client->context, client->fh, offset);
     if (ret < 0) {
         error_setg_errno(errp, -ret, "Failed to truncate file");
diff --git a/block/qcow2.c b/block/qcow2.c
index b69c88d..ed663c5 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2597,12 +2597,19 @@ static coroutine_fn int qcow2_co_pdiscard(BlockDriverState *bs,
     return ret;
 }
 
-static int qcow2_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
+static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
+                          PreallocMode prealloc, Error **errp)
 {
     BDRVQcow2State *s = bs->opaque;
     int64_t new_l1_size;
     int ret;
 
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
     if (offset & 511) {
         error_setg(errp, "The new size must be a multiple of 512");
         return -EINVAL;
diff --git a/block/qed.c b/block/qed.c
index fd76817..6d460ce 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -1518,12 +1518,19 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs,
     return cb.ret;
 }
 
-static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
+static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset,
+                             PreallocMode prealloc, Error **errp)
 {
     BDRVQEDState *s = bs->opaque;
     uint64_t old_image_size;
     int ret;
 
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
     if (!qed_is_image_size_valid(offset, s->header.cluster_size,
                                  s->header.table_size)) {
         error_setg(errp, "Invalid image size specified");
diff --git a/block/raw-format.c b/block/raw-format.c
index ac7453e..20134cb 100644
--- a/block/raw-format.c
+++ b/block/raw-format.c
@@ -352,10 +352,17 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
     }
 }
 
-static int raw_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
+static int raw_truncate(BlockDriverState *bs, int64_t offset,
+                        PreallocMode prealloc, Error **errp)
 {
     BDRVRawState *s = bs->opaque;
 
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
     if (s->has_size) {
         error_setg(errp, "Cannot resize fixed-size raw disks");
         return -ENOTSUP;
diff --git a/block/rbd.c b/block/rbd.c
index e551639..4b3fadb 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -916,11 +916,18 @@ static int64_t qemu_rbd_getlength(BlockDriverState *bs)
     return info.size;
 }
 
-static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
+static int qemu_rbd_truncate(BlockDriverState *bs, int64_t offset,
+                             PreallocMode prealloc, Error **errp)
 {
     BDRVRBDState *s = bs->opaque;
     int r;
 
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
     r = rbd_resize(s->image, offset);
     if (r < 0) {
         error_setg_errno(errp, -r, "Failed to resize file");
diff --git a/block/sheepdog.c b/block/sheepdog.c
index a18315a..e3e4ad5 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -2153,13 +2153,20 @@ static int64_t sd_getlength(BlockDriverState *bs)
     return s->inode.vdi_size;
 }
 
-static int sd_truncate(BlockDriverState *bs, int64_t offset, Error **errp)
+static int sd_truncate(BlockDriverState *bs, int64_t offset,
+                       PreallocMode prealloc, Error **errp)
 {
     BDRVSheepdogState *s = bs->opaque;
     int ret, fd;
     unsigned int datalen;
     uint64_t max_vdi_size;
 
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Unsupported preallocation mode '%s'",
+                   PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
     max_vdi_size = (UINT64_C(1) << s->inode.block_size_shift) * MAX_DATA_OBJS;
     if (offset < s->inode.vdi_size) {
         error_setg(errp, "shrinking is not supported");
@@ -2448,7 +2455,7 @@ static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num,
     BDRVSheepdogState *s = bs->opaque;
 
     if (offset > s->inode.vdi_size) {
-        ret = sd_truncate(bs, offset, NULL);
+        ret = sd_truncate(bs, offset, PREALLOC_MODE_OFF, NULL);
         if (ret < 0) {
             return ret;
         }
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 02/16] block: Add PreallocMode to bdrv_truncate()
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 01/16] block: Add PreallocMode to BD.bdrv_truncate() Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 03/16] block: Add PreallocMode to blk_truncate() Max Reitz
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

For block drivers that just pass a truncate request to the underlying
protocol, we can now pass the preallocation mode instead of aborting if
it is not PREALLOC_MODE_OFF.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 include/block/block.h  |  3 ++-
 block.c                |  5 +++--
 block/blkdebug.c       |  8 +-------
 block/block-backend.c  |  2 +-
 block/crypto.c         |  8 +-------
 block/parallels.c      | 11 +++++++----
 block/qcow.c           |  6 ++++--
 block/qcow2-refcount.c |  2 +-
 block/qcow2.c          |  4 ++--
 block/raw-format.c     |  8 +-------
 block/vhdx-log.c       |  2 +-
 block/vhdx.c           |  3 ++-
 12 files changed, 26 insertions(+), 36 deletions(-)

diff --git a/include/block/block.h b/include/block/block.h
index e2c6a0d..06b2f42 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -300,7 +300,8 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs,
     const char *backing_file);
 int bdrv_get_backing_file_depth(BlockDriverState *bs);
 void bdrv_refresh_filename(BlockDriverState *bs);
-int bdrv_truncate(BdrvChild *child, int64_t offset, Error **errp);
+int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
+                  Error **errp);
 int64_t bdrv_nb_sectors(BlockDriverState *bs);
 int64_t bdrv_getlength(BlockDriverState *bs);
 int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
diff --git a/block.c b/block.c
index 0d8fe01..49e9e27 100644
--- a/block.c
+++ b/block.c
@@ -3354,7 +3354,8 @@ exit:
 /**
  * Truncate file to 'offset' bytes (needed only for file protocols)
  */
-int bdrv_truncate(BdrvChild *child, int64_t offset, Error **errp)
+int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc,
+                  Error **errp)
 {
     BlockDriverState *bs = child->bs;
     BlockDriver *drv = bs->drv;
@@ -3377,7 +3378,7 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, Error **errp)
 
     assert(!(bs->open_flags & BDRV_O_INACTIVE));
 
-    ret = drv->bdrv_truncate(bs, offset, PREALLOC_MODE_OFF, errp);
+    ret = drv->bdrv_truncate(bs, offset, prealloc, errp);
     if (ret == 0) {
         ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
         bdrv_dirty_bitmap_truncate(bs);
diff --git a/block/blkdebug.c b/block/blkdebug.c
index 00090aa..3f971a3 100644
--- a/block/blkdebug.c
+++ b/block/blkdebug.c
@@ -815,13 +815,7 @@ static int64_t blkdebug_getlength(BlockDriverState *bs)
 static int blkdebug_truncate(BlockDriverState *bs, int64_t offset,
                              PreallocMode prealloc, Error **errp)
 {
-    if (prealloc != PREALLOC_MODE_OFF) {
-        error_setg(errp, "Unsupported preallocation mode '%s'",
-                   PreallocMode_lookup[prealloc]);
-        return -ENOTSUP;
-    }
-
-    return bdrv_truncate(bs->file, offset, errp);
+    return bdrv_truncate(bs->file, offset, prealloc, errp);
 }
 
 static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options)
diff --git a/block/block-backend.c b/block/block-backend.c
index f3a6008..caa2f24 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -1778,7 +1778,7 @@ int blk_truncate(BlockBackend *blk, int64_t offset, Error **errp)
         return -ENOMEDIUM;
     }
 
-    return bdrv_truncate(blk->root, offset, errp);
+    return bdrv_truncate(blk->root, offset, PREALLOC_MODE_OFF, errp);
 }
 
 static void blk_pdiscard_entry(void *opaque)
diff --git a/block/crypto.c b/block/crypto.c
index 7047cb2..6370598 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -388,15 +388,9 @@ static int block_crypto_truncate(BlockDriverState *bs, int64_t offset,
     size_t payload_offset =
         qcrypto_block_get_payload_offset(crypto->block);
 
-    if (prealloc != PREALLOC_MODE_OFF) {
-        error_setg(errp, "Unsupported preallocation mode '%s'",
-                   PreallocMode_lookup[prealloc]);
-        return -ENOTSUP;
-    }
-
     offset += payload_offset;
 
-    return bdrv_truncate(bs->file, offset, errp);
+    return bdrv_truncate(bs->file, offset, prealloc, errp);
 }
 
 static void block_crypto_close(BlockDriverState *bs)
diff --git a/block/parallels.c b/block/parallels.c
index 8be46a7..5a38998 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -224,7 +224,7 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num,
         } else {
             ret = bdrv_truncate(bs->file,
                                 (s->data_end + space) << BDRV_SECTOR_BITS,
-                                NULL);
+                                PREALLOC_MODE_OFF, NULL);
         }
         if (ret < 0) {
             return ret;
@@ -458,7 +458,8 @@ static int parallels_check(BlockDriverState *bs, BdrvCheckResult *res,
         res->leaks += count;
         if (fix & BDRV_FIX_LEAKS) {
             Error *local_err = NULL;
-            ret = bdrv_truncate(bs->file, res->image_end_offset, &local_err);
+            ret = bdrv_truncate(bs->file, res->image_end_offset,
+                                PREALLOC_MODE_OFF, &local_err);
             if (ret < 0) {
                 error_report_err(local_err);
                 res->check_errors++;
@@ -699,7 +700,8 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
     }
 
     if (!(flags & BDRV_O_RESIZE) || !bdrv_has_zero_init(bs->file->bs) ||
-            bdrv_truncate(bs->file, bdrv_getlength(bs->file->bs), NULL) != 0) {
+            bdrv_truncate(bs->file, bdrv_getlength(bs->file->bs),
+                          PREALLOC_MODE_OFF, NULL) != 0) {
         s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE;
     }
 
@@ -742,7 +744,8 @@ static void parallels_close(BlockDriverState *bs)
     }
 
     if (bs->open_flags & BDRV_O_RDWR) {
-        bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS, NULL);
+        bdrv_truncate(bs->file, s->data_end << BDRV_SECTOR_BITS,
+                      PREALLOC_MODE_OFF, NULL);
     }
 
     g_free(s->bat_dirty_bmap);
diff --git a/block/qcow.c b/block/qcow.c
index 95ab123..b79d990 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -473,7 +473,8 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,
                 /* round to cluster size */
                 cluster_offset = (cluster_offset + s->cluster_size - 1) &
                     ~(s->cluster_size - 1);
-                bdrv_truncate(bs->file, cluster_offset + s->cluster_size, NULL);
+                bdrv_truncate(bs->file, cluster_offset + s->cluster_size,
+                              PREALLOC_MODE_OFF, NULL);
                 /* if encrypted, we must initialize the cluster
                    content which won't be written */
                 if (bs->encrypted &&
@@ -916,7 +917,8 @@ static int qcow_make_empty(BlockDriverState *bs)
     if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, s->l1_table,
             l1_length) < 0)
         return -1;
-    ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length, NULL);
+    ret = bdrv_truncate(bs->file, s->l1_table_offset + l1_length,
+                        PREALLOC_MODE_OFF, NULL);
     if (ret < 0)
         return ret;
 
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 7c06061..84e0cee 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -1730,7 +1730,7 @@ static int check_refblocks(BlockDriverState *bs, BdrvCheckResult *res,
                 }
 
                 ret = bdrv_truncate(bs->file, offset + s->cluster_size,
-                                    &local_err);
+                                    PREALLOC_MODE_OFF, &local_err);
                 if (ret < 0) {
                     error_report_err(local_err);
                     goto resize_fail;
diff --git a/block/qcow2.c b/block/qcow2.c
index ed663c5..8357cb8 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2665,7 +2665,7 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
         /* align end of file to a sector boundary to ease reading with
            sector based I/Os */
         cluster_offset = bdrv_getlength(bs->file->bs);
-        return bdrv_truncate(bs->file, cluster_offset, NULL);
+        return bdrv_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, NULL);
     }
 
     buf = qemu_blockalign(bs, s->cluster_size);
@@ -2881,7 +2881,7 @@ static int make_completely_empty(BlockDriverState *bs)
     }
 
     ret = bdrv_truncate(bs->file, (3 + l1_clusters) * s->cluster_size,
-                        &local_err);
+                        PREALLOC_MODE_OFF, &local_err);
     if (ret < 0) {
         error_report_err(local_err);
         goto fail;
diff --git a/block/raw-format.c b/block/raw-format.c
index 20134cb..83a0b33 100644
--- a/block/raw-format.c
+++ b/block/raw-format.c
@@ -357,12 +357,6 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset,
 {
     BDRVRawState *s = bs->opaque;
 
-    if (prealloc != PREALLOC_MODE_OFF) {
-        error_setg(errp, "Unsupported preallocation mode '%s'",
-                   PreallocMode_lookup[prealloc]);
-        return -ENOTSUP;
-    }
-
     if (s->has_size) {
         error_setg(errp, "Cannot resize fixed-size raw disks");
         return -ENOTSUP;
@@ -375,7 +369,7 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset,
 
     s->size = offset;
     offset += s->offset;
-    return bdrv_truncate(bs->file, offset, errp);
+    return bdrv_truncate(bs->file, offset, prealloc, errp);
 }
 
 static int raw_media_changed(BlockDriverState *bs)
diff --git a/block/vhdx-log.c b/block/vhdx-log.c
index 3f4c2aa..01278f3 100644
--- a/block/vhdx-log.c
+++ b/block/vhdx-log.c
@@ -548,7 +548,7 @@ static int vhdx_log_flush(BlockDriverState *bs, BDRVVHDXState *s,
             if (new_file_size % (1024*1024)) {
                 /* round up to nearest 1MB boundary */
                 new_file_size = ((new_file_size >> 20) + 1) << 20;
-                bdrv_truncate(bs->file, new_file_size, NULL);
+                bdrv_truncate(bs->file, new_file_size, PREALLOC_MODE_OFF, NULL);
             }
         }
         qemu_vfree(desc_entries);
diff --git a/block/vhdx.c b/block/vhdx.c
index 8b270b5..85c476a 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -1171,7 +1171,8 @@ static int vhdx_allocate_block(BlockDriverState *bs, BDRVVHDXState *s,
     /* per the spec, the address for a block is in units of 1MB */
     *new_offset = ROUND_UP(*new_offset, 1024 * 1024);
 
-    return bdrv_truncate(bs->file, *new_offset + s->block_size, NULL);
+    return bdrv_truncate(bs->file, *new_offset + s->block_size,
+                         PREALLOC_MODE_OFF, NULL);
 }
 
 /*
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 03/16] block: Add PreallocMode to blk_truncate()
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 01/16] block: Add PreallocMode to BD.bdrv_truncate() Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 02/16] block: Add PreallocMode to bdrv_truncate() Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 04/16] qemu-img: Expose PreallocMode for resizing Max Reitz
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

blk_truncate() itself will pass that value to bdrv_truncate(), and all
callers of blk_truncate() just set the parameter to PREALLOC_MODE_OFF
for now.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 include/sysemu/block-backend.h | 3 ++-
 block/block-backend.c          | 5 +++--
 block/commit.c                 | 4 ++--
 block/mirror.c                 | 3 ++-
 block/parallels.c              | 2 +-
 block/qcow.c                   | 2 +-
 block/qcow2.c                  | 4 ++--
 block/qed.c                    | 2 +-
 block/vdi.c                    | 3 ++-
 block/vhdx.c                   | 5 +++--
 block/vmdk.c                   | 7 ++++---
 block/vpc.c                    | 2 +-
 blockdev.c                     | 2 +-
 qemu-img.c                     | 2 +-
 qemu-io-cmds.c                 | 2 +-
 15 files changed, 27 insertions(+), 21 deletions(-)

diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
index 840ad61..1f63ec3 100644
--- a/include/sysemu/block-backend.h
+++ b/include/sysemu/block-backend.h
@@ -225,7 +225,8 @@ int coroutine_fn blk_co_pwrite_zeroes(BlockBackend *blk, int64_t offset,
                                       int count, BdrvRequestFlags flags);
 int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf,
                           int count);
-int blk_truncate(BlockBackend *blk, int64_t offset, Error **errp);
+int blk_truncate(BlockBackend *blk, int64_t offset, PreallocMode prealloc,
+                 Error **errp);
 int blk_pdiscard(BlockBackend *blk, int64_t offset, int count);
 int blk_save_vmstate(BlockBackend *blk, const uint8_t *buf,
                      int64_t pos, int size);
diff --git a/block/block-backend.c b/block/block-backend.c
index caa2f24..2bc3162 100644
--- a/block/block-backend.c
+++ b/block/block-backend.c
@@ -1771,14 +1771,15 @@ int blk_pwrite_compressed(BlockBackend *blk, int64_t offset, const void *buf,
                    BDRV_REQ_WRITE_COMPRESSED);
 }
 
-int blk_truncate(BlockBackend *blk, int64_t offset, Error **errp)
+int blk_truncate(BlockBackend *blk, int64_t offset, PreallocMode prealloc,
+                 Error **errp)
 {
     if (!blk_is_available(blk)) {
         error_setg(errp, "No medium inserted");
         return -ENOMEDIUM;
     }
 
-    return bdrv_truncate(blk->root, offset, PREALLOC_MODE_OFF, errp);
+    return bdrv_truncate(blk->root, offset, prealloc, errp);
 }
 
 static void blk_pdiscard_entry(void *opaque)
diff --git a/block/commit.c b/block/commit.c
index 76a0d98..2076a37 100644
--- a/block/commit.c
+++ b/block/commit.c
@@ -151,7 +151,7 @@ static void coroutine_fn commit_run(void *opaque)
     }
 
     if (base_len < s->common.len) {
-        ret = blk_truncate(s->base, s->common.len, NULL);
+        ret = blk_truncate(s->base, s->common.len, PREALLOC_MODE_OFF, NULL);
         if (ret) {
             goto out;
         }
@@ -511,7 +511,7 @@ int bdrv_commit(BlockDriverState *bs)
      * grow the backing file image if possible.  If not possible,
      * we must return an error */
     if (length > backing_length) {
-        ret = blk_truncate(backing, length, &local_err);
+        ret = blk_truncate(backing, length, PREALLOC_MODE_OFF, &local_err);
         if (ret < 0) {
             error_report_err(local_err);
             goto ro_cleanup;
diff --git a/block/mirror.c b/block/mirror.c
index e86f8f8..dad2bdd 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -724,7 +724,8 @@ static void coroutine_fn mirror_run(void *opaque)
         }
 
         if (s->bdev_length > base_length) {
-            ret = blk_truncate(s->target, s->bdev_length, NULL);
+            ret = blk_truncate(s->target, s->bdev_length, PREALLOC_MODE_OFF,
+                               NULL);
             if (ret < 0) {
                 goto immediate_exit;
             }
diff --git a/block/parallels.c b/block/parallels.c
index 5a38998..5bbdfab 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -508,7 +508,7 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp)
 
     blk_set_allow_write_beyond_eof(file, true);
 
-    ret = blk_truncate(file, 0, errp);
+    ret = blk_truncate(file, 0, PREALLOC_MODE_OFF, errp);
     if (ret < 0) {
         goto exit;
     }
diff --git a/block/qcow.c b/block/qcow.c
index b79d990..f7703ab 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -834,7 +834,7 @@ static int qcow_create(const char *filename, QemuOpts *opts, Error **errp)
 
     blk_set_allow_write_beyond_eof(qcow_blk, true);
 
-    ret = blk_truncate(qcow_blk, 0, errp);
+    ret = blk_truncate(qcow_blk, 0, PREALLOC_MODE_OFF, errp);
     if (ret < 0) {
         goto exit;
     }
diff --git a/block/qcow2.c b/block/qcow2.c
index 8357cb8..3d4f36d 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2364,7 +2364,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
     }
 
     /* Okay, now that we have a valid image, let's give it the right size */
-    ret = blk_truncate(blk, total_size, errp);
+    ret = blk_truncate(blk, total_size, PREALLOC_MODE_OFF, errp);
     if (ret < 0) {
         error_prepend(errp, "Could not resize image: ");
         goto out;
@@ -3478,7 +3478,7 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
             return ret;
         }
 
-        ret = blk_truncate(blk, new_size, &local_err);
+        ret = blk_truncate(blk, new_size, PREALLOC_MODE_OFF, &local_err);
         blk_unref(blk);
         if (ret < 0) {
             error_report_err(local_err);
diff --git a/block/qed.c b/block/qed.c
index 6d460ce..2feaba3 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -635,7 +635,7 @@ static int qed_create(const char *filename, uint32_t cluster_size,
     blk_set_allow_write_beyond_eof(blk, true);
 
     /* File must start empty and grow, check truncate is supported */
-    ret = blk_truncate(blk, 0, errp);
+    ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
     if (ret < 0) {
         goto out;
     }
diff --git a/block/vdi.c b/block/vdi.c
index 79af477..2b6e8fa 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -832,7 +832,8 @@ static int vdi_create(const char *filename, QemuOpts *opts, Error **errp)
     }
 
     if (image_type == VDI_TYPE_STATIC) {
-        ret = blk_truncate(blk, offset + blocks * block_size, errp);
+        ret = blk_truncate(blk, offset + blocks * block_size,
+                           PREALLOC_MODE_OFF, errp);
         if (ret < 0) {
             error_prepend(errp, "Failed to statically allocate %s", filename);
             goto exit;
diff --git a/block/vhdx.c b/block/vhdx.c
index 85c476a..a9cecd2 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -1608,12 +1608,13 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s,
     if (type == VHDX_TYPE_DYNAMIC) {
         /* All zeroes, so we can just extend the file - the end of the BAT
          * is the furthest thing we have written yet */
-        ret = blk_truncate(blk, data_file_offset, errp);
+        ret = blk_truncate(blk, data_file_offset, PREALLOC_MODE_OFF, errp);
         if (ret < 0) {
             goto exit;
         }
     } else if (type == VHDX_TYPE_FIXED) {
-        ret = blk_truncate(blk, data_file_offset + image_size, errp);
+        ret = blk_truncate(blk, data_file_offset + image_size,
+                           PREALLOC_MODE_OFF, errp);
         if (ret < 0) {
             goto exit;
         }
diff --git a/block/vmdk.c b/block/vmdk.c
index 55581b0..24d71b5 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -1714,7 +1714,7 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
     blk_set_allow_write_beyond_eof(blk, true);
 
     if (flat) {
-        ret = blk_truncate(blk, filesize, errp);
+        ret = blk_truncate(blk, filesize, PREALLOC_MODE_OFF, errp);
         goto exit;
     }
     magic = cpu_to_be32(VMDK4_MAGIC);
@@ -1777,7 +1777,8 @@ static int vmdk_create_extent(const char *filename, int64_t filesize,
         goto exit;
     }
 
-    ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9, errp);
+    ret = blk_truncate(blk, le64_to_cpu(header.grain_offset) << 9,
+                       PREALLOC_MODE_OFF, errp);
     if (ret < 0) {
         goto exit;
     }
@@ -2086,7 +2087,7 @@ static int vmdk_create(const char *filename, QemuOpts *opts, Error **errp)
     /* bdrv_pwrite write padding zeros to align to sector, we don't need that
      * for description file */
     if (desc_offset == 0) {
-        ret = blk_truncate(new_blk, desc_len, errp);
+        ret = blk_truncate(new_blk, desc_len, PREALLOC_MODE_OFF, errp);
     }
 exit:
     if (new_blk) {
diff --git a/block/vpc.c b/block/vpc.c
index 4240ba9..a72982a 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -858,7 +858,7 @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
     /* Add footer to total size */
     total_size += HEADER_SIZE;
 
-    ret = blk_truncate(blk, total_size, errp);
+    ret = blk_truncate(blk, total_size, PREALLOC_MODE_OFF, errp);
     if (ret < 0) {
         return ret;
     }
diff --git a/blockdev.c b/blockdev.c
index c63f4e8..042c12c 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -2924,7 +2924,7 @@ void qmp_block_resize(bool has_device, const char *device,
     }
 
     bdrv_drained_begin(bs);
-    ret = blk_truncate(blk, size, errp);
+    ret = blk_truncate(blk, size, PREALLOC_MODE_OFF, errp);
     bdrv_drained_end(bs);
 
 out:
diff --git a/qemu-img.c b/qemu-img.c
index 4f62dc1..236fd2e 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -3564,7 +3564,7 @@ static int img_resize(int argc, char **argv)
         goto out;
     }
 
-    ret = blk_truncate(blk, total_size, &err);
+    ret = blk_truncate(blk, total_size, PREALLOC_MODE_OFF, &err);
     if (!ret) {
         qprintf(quiet, "Image resized.\n");
     } else {
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
index 4b2278f..0f79088 100644
--- a/qemu-io-cmds.c
+++ b/qemu-io-cmds.c
@@ -1577,7 +1577,7 @@ static int truncate_f(BlockBackend *blk, int argc, char **argv)
         return 0;
     }
 
-    ret = blk_truncate(blk, offset, &local_err);
+    ret = blk_truncate(blk, offset, PREALLOC_MODE_OFF, &local_err);
     if (ret < 0) {
         error_report_err(local_err);
         return 0;
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 04/16] qemu-img: Expose PreallocMode for resizing
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (2 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 03/16] block: Add PreallocMode to blk_truncate() Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-30 20:57   ` Eric Blake
  2017-05-31 10:09   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 05/16] block/file-posix: Small fixes in raw_create() Max Reitz
                   ` (11 subsequent siblings)
  15 siblings, 2 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

Add a --preallocation command line option to qemu-img resize which can
be used to set the PreallocMode parameter of blk_truncate().

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 qemu-img.c    | 33 ++++++++++++++++++++++++++++++---
 qemu-img.texi |  7 ++++++-
 2 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/qemu-img.c b/qemu-img.c
index 236fd2e..7463424 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -24,6 +24,7 @@
 #include "qemu/osdep.h"
 #include "qemu-version.h"
 #include "qapi/error.h"
+#include "qapi/util.h"
 #include "qapi-visit.h"
 #include "qapi/qobject-output-visitor.h"
 #include "qapi/qmp/qerror.h"
@@ -62,6 +63,7 @@ enum {
     OPTION_NO_DRAIN = 262,
     OPTION_TARGET_IMAGE_OPTS = 263,
     OPTION_SIZE = 264,
+    OPTION_PREALLOCATION = 265,
 };
 
 typedef enum OutputFormat {
@@ -3435,9 +3437,10 @@ static int img_resize(int argc, char **argv)
     Error *err = NULL;
     int c, ret, relative;
     const char *filename, *fmt, *size;
-    int64_t n, total_size;
+    int64_t n, total_size, current_size;
     bool quiet = false;
     BlockBackend *blk = NULL;
+    PreallocMode prealloc = PREALLOC_MODE_OFF;
     QemuOpts *param;
 
     static QemuOptsList resize_options = {
@@ -3471,6 +3474,7 @@ static int img_resize(int argc, char **argv)
             {"help", no_argument, 0, 'h'},
             {"object", required_argument, 0, OPTION_OBJECT},
             {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
+            {"preallocation", required_argument, 0, OPTION_PREALLOCATION},
             {0, 0, 0, 0}
         };
         c = getopt_long(argc, argv, ":f:hq",
@@ -3505,6 +3509,15 @@ static int img_resize(int argc, char **argv)
         case OPTION_IMAGE_OPTS:
             image_opts = true;
             break;
+        case OPTION_PREALLOCATION:
+            prealloc = qapi_enum_parse(PreallocMode_lookup, optarg,
+                                       PREALLOC_MODE__MAX, PREALLOC_MODE__MAX,
+                                       NULL);
+            if (prealloc == PREALLOC_MODE__MAX) {
+                error_report("Invalid preallocation mode '%s'", optarg);
+                return 1;
+            }
+            break;
         }
     }
     if (optind != argc - 1) {
@@ -3553,8 +3566,16 @@ static int img_resize(int argc, char **argv)
         goto out;
     }
 
+    current_size = blk_getlength(blk);
+    if (current_size < 0) {
+        error_report("Failed to inquire current image length: %s",
+                     strerror(-current_size));
+        ret = -1;
+        goto out;
+    }
+
     if (relative) {
-        total_size = blk_getlength(blk) + n * relative;
+        total_size = current_size + n * relative;
     } else {
         total_size = n;
     }
@@ -3564,7 +3585,13 @@ static int img_resize(int argc, char **argv)
         goto out;
     }
 
-    ret = blk_truncate(blk, total_size, PREALLOC_MODE_OFF, &err);
+    if (total_size <= current_size && prealloc != PREALLOC_MODE_OFF) {
+        error_report("Preallocation can only be used for growing images");
+        ret = -1;
+        goto out;
+    }
+
+    ret = blk_truncate(blk, total_size, prealloc, &err);
     if (!ret) {
         qprintf(quiet, "Image resized.\n");
     } else {
diff --git a/qemu-img.texi b/qemu-img.texi
index 3db035f..64bcf87 100644
--- a/qemu-img.texi
+++ b/qemu-img.texi
@@ -529,7 +529,7 @@ qemu-img rebase -b base.img diff.qcow2
 At this point, @code{modified.img} can be discarded, since
 @code{base.img + diff.qcow2} contains the same information.
 
-@item resize @var{filename} [+ | -]@var{size}
+@item resize [--preallocation=@var{prealloc}] @var{filename} [+ | -]@var{size}
 
 Change the disk image as if it had been created with @var{size}.
 
@@ -541,6 +541,11 @@ After using this command to grow a disk image, you must use file system and
 partitioning tools inside the VM to actually begin using the new space on the
 device.
 
+When growing an image, the @code{--preallocation} option may be used to specify
+how the additional image area should be allocated on the host.  See the format
+description in the @code{NOTES} section which values are allowed.  Using this
+option may result in more data being allocated than necessary.
+
 @item amend [-p] [-f @var{fmt}] [-t @var{cache}] -o @var{options} @var{filename}
 
 Amends the image format specific @var{options} for the image file
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 05/16] block/file-posix: Small fixes in raw_create()
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (3 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 04/16] qemu-img: Expose PreallocMode for resizing Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 06/16] block/file-posix: Extract raw_regular_truncate() Max Reitz
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

Variables should be declared at the start of a block, and if a certain
parameter value is not supported it may be better to return -ENOTSUP
instead of -EINVAL.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
---
 block/file-posix.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/block/file-posix.c b/block/file-posix.c
index 7cc6bf6..f5a662e 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -1915,6 +1915,8 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
 #endif
     case PREALLOC_MODE_FULL:
     {
+        int64_t num = 0, left = total_size;
+
         /*
          * Knowing the final size from the beginning could allow the file
          * system driver to do less allocations and possibly avoid
@@ -1926,7 +1928,6 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
             goto out_close;
         }
 
-        int64_t num = 0, left = total_size;
         buf = g_malloc0(65536);
 
         while (left > 0) {
@@ -1958,7 +1959,7 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
         }
         break;
     default:
-        result = -EINVAL;
+        result = -ENOTSUP;
         error_setg(errp, "Unsupported preallocation mode: %s",
                    PreallocMode_lookup[prealloc]);
         break;
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 06/16] block/file-posix: Extract raw_regular_truncate()
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (4 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 05/16] block/file-posix: Small fixes in raw_create() Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 07/16] block/file-posix: Generalize raw_regular_truncate Max Reitz
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

This functionality is part of raw_create() which we will be able to
reuse nicely in raw_truncate().

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
---
 block/file-posix.c | 144 +++++++++++++++++++++++++++++------------------------
 1 file changed, 78 insertions(+), 66 deletions(-)

diff --git a/block/file-posix.c b/block/file-posix.c
index f5a662e..91a2dc6 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -1629,6 +1629,81 @@ static void raw_close(BlockDriverState *bs)
     }
 }
 
+static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
+                                Error **errp)
+{
+    int result = 0;
+    char *buf;
+
+    switch (prealloc) {
+#ifdef CONFIG_POSIX_FALLOCATE
+    case PREALLOC_MODE_FALLOC:
+        /*
+         * Truncating before posix_fallocate() makes it about twice slower on
+         * file systems that do not support fallocate(), trying to check if a
+         * block is allocated before allocating it, so don't do that here.
+         */
+        result = -posix_fallocate(fd, 0, offset);
+        if (result != 0) {
+            /* posix_fallocate() doesn't set errno. */
+            error_setg_errno(errp, -result,
+                             "Could not preallocate data for the new file");
+        }
+        return result;
+#endif
+    case PREALLOC_MODE_FULL:
+    {
+        int64_t num = 0, left = offset;
+
+        /*
+         * Knowing the final size from the beginning could allow the file
+         * system driver to do less allocations and possibly avoid
+         * fragmentation of the file.
+         */
+        if (ftruncate(fd, offset) != 0) {
+            result = -errno;
+            error_setg_errno(errp, -result, "Could not resize file");
+            return result;
+        }
+
+        buf = g_malloc0(65536);
+
+        while (left > 0) {
+            num = MIN(left, 65536);
+            result = write(fd, buf, num);
+            if (result < 0) {
+                result = -errno;
+                error_setg_errno(errp, -result,
+                                 "Could not write to the new file");
+                break;
+            }
+            left -= result;
+        }
+        if (result >= 0) {
+            result = fsync(fd);
+            if (result < 0) {
+                result = -errno;
+                error_setg_errno(errp, -result,
+                                 "Could not flush new file to disk");
+            }
+        }
+        g_free(buf);
+        return result;
+    }
+    case PREALLOC_MODE_OFF:
+        if (ftruncate(fd, offset) != 0) {
+            result = -errno;
+            error_setg_errno(errp, -result, "Could not resize file");
+        }
+        return result;
+    default:
+        result = -ENOTSUP;
+        error_setg(errp, "Unsupported preallocation mode: %s",
+                   PreallocMode_lookup[prealloc]);
+        return result;
+    }
+}
+
 static int raw_truncate(BlockDriverState *bs, int64_t offset,
                         PreallocMode prealloc, Error **errp)
 {
@@ -1897,72 +1972,9 @@ static int raw_create(const char *filename, QemuOpts *opts, Error **errp)
 #endif
     }
 
-    switch (prealloc) {
-#ifdef CONFIG_POSIX_FALLOCATE
-    case PREALLOC_MODE_FALLOC:
-        /*
-         * Truncating before posix_fallocate() makes it about twice slower on
-         * file systems that do not support fallocate(), trying to check if a
-         * block is allocated before allocating it, so don't do that here.
-         */
-        result = -posix_fallocate(fd, 0, total_size);
-        if (result != 0) {
-            /* posix_fallocate() doesn't set errno. */
-            error_setg_errno(errp, -result,
-                             "Could not preallocate data for the new file");
-        }
-        break;
-#endif
-    case PREALLOC_MODE_FULL:
-    {
-        int64_t num = 0, left = total_size;
-
-        /*
-         * Knowing the final size from the beginning could allow the file
-         * system driver to do less allocations and possibly avoid
-         * fragmentation of the file.
-         */
-        if (ftruncate(fd, total_size) != 0) {
-            result = -errno;
-            error_setg_errno(errp, -result, "Could not resize file");
-            goto out_close;
-        }
-
-        buf = g_malloc0(65536);
-
-        while (left > 0) {
-            num = MIN(left, 65536);
-            result = write(fd, buf, num);
-            if (result < 0) {
-                result = -errno;
-                error_setg_errno(errp, -result,
-                                 "Could not write to the new file");
-                break;
-            }
-            left -= result;
-        }
-        if (result >= 0) {
-            result = fsync(fd);
-            if (result < 0) {
-                result = -errno;
-                error_setg_errno(errp, -result,
-                                 "Could not flush new file to disk");
-            }
-        }
-        g_free(buf);
-        break;
-    }
-    case PREALLOC_MODE_OFF:
-        if (ftruncate(fd, total_size) != 0) {
-            result = -errno;
-            error_setg_errno(errp, -result, "Could not resize file");
-        }
-        break;
-    default:
-        result = -ENOTSUP;
-        error_setg(errp, "Unsupported preallocation mode: %s",
-                   PreallocMode_lookup[prealloc]);
-        break;
+    result = raw_regular_truncate(fd, total_size, prealloc, errp);
+    if (result < 0) {
+        goto out_close;
     }
 
 out_close:
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 07/16] block/file-posix: Generalize raw_regular_truncate
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (5 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 06/16] block/file-posix: Extract raw_regular_truncate() Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 08/16] block/file-posix: Preallocation for truncate Max Reitz
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

Currently, raw_regular_truncate() is intended for setting the size of a
newly created file. However, we also want to use it for truncating an
existing file in which case only the newly added space (when growing)
should be preallocated.

This also means that if resizing failed, we should try to restore the
original file size. This is important when using preallocation.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 block/file-posix.c | 61 ++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 50 insertions(+), 11 deletions(-)

diff --git a/block/file-posix.c b/block/file-posix.c
index 91a2dc6..e38c226 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -1629,11 +1629,31 @@ static void raw_close(BlockDriverState *bs)
     }
 }
 
+/**
+ * Truncates the given regular file @fd to @offset and, when growing, fills the
+ * new space according to @prealloc.
+ *
+ * Returns: 0 on success, -errno on failure.
+ */
 static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
                                 Error **errp)
 {
     int result = 0;
-    char *buf;
+    int64_t current_length = 0;
+    char *buf = NULL;
+    struct stat st;
+
+    if (fstat(fd, &st) < 0) {
+        result = -errno;
+        error_setg_errno(errp, -result, "Could not stat file");
+        return result;
+    }
+
+    current_length = st.st_size;
+    if (current_length > offset && prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Cannot use preallocation for shrinking files");
+        return -ENOTSUP;
+    }
 
     switch (prealloc) {
 #ifdef CONFIG_POSIX_FALLOCATE
@@ -1643,17 +1663,17 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
          * file systems that do not support fallocate(), trying to check if a
          * block is allocated before allocating it, so don't do that here.
          */
-        result = -posix_fallocate(fd, 0, offset);
+        result = -posix_fallocate(fd, current_length, offset - current_length);
         if (result != 0) {
             /* posix_fallocate() doesn't set errno. */
             error_setg_errno(errp, -result,
-                             "Could not preallocate data for the new file");
+                             "Could not preallocate new data");
         }
-        return result;
+        goto out;
 #endif
     case PREALLOC_MODE_FULL:
     {
-        int64_t num = 0, left = offset;
+        int64_t num = 0, left = offset - current_length;
 
         /*
          * Knowing the final size from the beginning could allow the file
@@ -1663,19 +1683,27 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
         if (ftruncate(fd, offset) != 0) {
             result = -errno;
             error_setg_errno(errp, -result, "Could not resize file");
-            return result;
+            goto out;
         }
 
         buf = g_malloc0(65536);
 
+        result = lseek(fd, current_length, SEEK_SET);
+        if (result < 0) {
+            result = -errno;
+            error_setg_errno(errp, -result,
+                             "Failed to seek to the old end of file");
+            goto out;
+        }
+
         while (left > 0) {
             num = MIN(left, 65536);
             result = write(fd, buf, num);
             if (result < 0) {
                 result = -errno;
                 error_setg_errno(errp, -result,
-                                 "Could not write to the new file");
-                break;
+                                 "Could not write zeros for preallocation");
+                goto out;
             }
             left -= result;
         }
@@ -1684,11 +1712,11 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
             if (result < 0) {
                 result = -errno;
                 error_setg_errno(errp, -result,
-                                 "Could not flush new file to disk");
+                                 "Could not flush file to disk");
+                goto out;
             }
         }
-        g_free(buf);
-        return result;
+        goto out;
     }
     case PREALLOC_MODE_OFF:
         if (ftruncate(fd, offset) != 0) {
@@ -1702,6 +1730,17 @@ static int raw_regular_truncate(int fd, int64_t offset, PreallocMode prealloc,
                    PreallocMode_lookup[prealloc]);
         return result;
     }
+
+out:
+    if (result < 0) {
+        if (ftruncate(fd, current_length) < 0) {
+            error_report("Failed to restore old file length: %s",
+                         strerror(errno));
+        }
+    }
+
+    g_free(buf);
+    return result;
 }
 
 static int raw_truncate(BlockDriverState *bs, int64_t offset,
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 08/16] block/file-posix: Preallocation for truncate
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (6 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 07/16] block/file-posix: Generalize raw_regular_truncate Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 09/16] block/qcow2: Generalize preallocate() Max Reitz
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

By using raw_regular_truncate() in raw_truncate(), we can now easily
support preallocation.

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 block/file-posix.c | 22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/block/file-posix.c b/block/file-posix.c
index e38c226..0f51596 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -1750,12 +1750,6 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset,
     struct stat st;
     int ret;
 
-    if (prealloc != PREALLOC_MODE_OFF) {
-        error_setg(errp, "Unsupported preallocation mode '%s'",
-                   PreallocMode_lookup[prealloc]);
-        return -ENOTSUP;
-    }
-
     if (fstat(s->fd, &st)) {
         ret = -errno;
         error_setg_errno(errp, -ret, "Failed to fstat() the file");
@@ -1763,12 +1757,16 @@ static int raw_truncate(BlockDriverState *bs, int64_t offset,
     }
 
     if (S_ISREG(st.st_mode)) {
-        if (ftruncate(s->fd, offset) < 0) {
-            ret = -errno;
-            error_setg_errno(errp, -ret, "Failed to resize the file");
-            return ret;
-        }
-    } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
+        return raw_regular_truncate(s->fd, offset, prealloc, errp);
+    }
+
+    if (prealloc != PREALLOC_MODE_OFF) {
+        error_setg(errp, "Preallocation mode '%s' unsupported for this "
+                   "non-regular file", PreallocMode_lookup[prealloc]);
+        return -ENOTSUP;
+    }
+
+    if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
         if (offset > raw_getlength(bs)) {
             error_setg(errp, "Cannot grow device files");
             return -EINVAL;
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 09/16] block/qcow2: Generalize preallocate()
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (7 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 08/16] block/file-posix: Preallocation for truncate Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-30 21:26   ` Eric Blake
  2017-05-31 10:22   ` Stefan Hajnoczi
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 10/16] block/qcow2: Lock s->lock in preallocate() Max Reitz
                   ` (6 subsequent siblings)
  15 siblings, 2 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

This patch adds two new parameters to the preallocate() function so we
will be able to use it not just for preallocating a new image but also
for preallocated image growth.

The offset parameter allows the caller to specify a virtual offset from
which to start preallocating. For newly created images this is always 0,
but for preallocating growth this will be the old image length.

The new_length parameter specifies the supposed new length of the image
(basically the "end offset" for preallocation). During image truncation,
bdrv_getlength() will return the old image length so we cannot rely on
its return value then.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 block/qcow2.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index 3d4f36d..df953aa 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2035,17 +2035,24 @@ static int qcow2_change_backing_file(BlockDriverState *bs,
     return qcow2_update_header(bs);
 }
 
-static int preallocate(BlockDriverState *bs)
+/**
+ * Preallocates metadata structures for data clusters between @offset (in the
+ * guest disk) and @new_length (which is thus generally the new guest disk
+ * size).
+ *
+ * Returns: 0 on success, -errno on failure.
+ */
+static int preallocate(BlockDriverState *bs,
+                       uint64_t offset, uint64_t new_length)
 {
     uint64_t bytes;
-    uint64_t offset;
     uint64_t host_offset = 0;
     unsigned int cur_bytes;
     int ret;
     QCowL2Meta *meta;
 
-    bytes = bdrv_getlength(bs);
-    offset = 0;
+    assert(offset <= new_length);
+    bytes = new_length - offset;
 
     while (bytes) {
         cur_bytes = MIN(bytes, INT_MAX);
@@ -2384,7 +2391,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
     if (prealloc != PREALLOC_MODE_OFF) {
         BDRVQcow2State *s = blk_bs(blk)->opaque;
         qemu_co_mutex_lock(&s->lock);
-        ret = preallocate(blk_bs(blk));
+        ret = preallocate(blk_bs(blk), 0, total_size);
         qemu_co_mutex_unlock(&s->lock);
         if (ret < 0) {
             error_setg_errno(errp, -ret, "Could not preallocate metadata");
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 10/16] block/qcow2: Lock s->lock in preallocate()
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (8 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 09/16] block/qcow2: Generalize preallocate() Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-30 21:28   ` Eric Blake
  2017-05-31 10:24   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 11/16] block/qcow2: Metadata preallocation for truncate Max Reitz
                   ` (5 subsequent siblings)
  15 siblings, 2 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

preallocate() is and will be called only from places that do not lock
s->lock: Currently that is qcow2_create2(), as of a future patch it will
be called from qcow2_truncate(), too.

It therefore makes sense to move locking that mutex into preallocate()
itself.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 block/qcow2.c | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index df953aa..a8223a0 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2045,12 +2045,17 @@ static int qcow2_change_backing_file(BlockDriverState *bs,
 static int preallocate(BlockDriverState *bs,
                        uint64_t offset, uint64_t new_length)
 {
+    BDRVQcow2State *s = bs->opaque;
     uint64_t bytes;
     uint64_t host_offset = 0;
     unsigned int cur_bytes;
     int ret;
     QCowL2Meta *meta;
 
+    if (qemu_in_coroutine()) {
+        qemu_co_mutex_lock(&s->lock);
+    }
+
     assert(offset <= new_length);
     bytes = new_length - offset;
 
@@ -2059,7 +2064,7 @@ static int preallocate(BlockDriverState *bs,
         ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes,
                                          &host_offset, &meta);
         if (ret < 0) {
-            return ret;
+            goto done;
         }
 
         while (meta) {
@@ -2069,7 +2074,7 @@ static int preallocate(BlockDriverState *bs,
             if (ret < 0) {
                 qcow2_free_any_clusters(bs, meta->alloc_offset,
                                         meta->nb_clusters, QCOW2_DISCARD_NEVER);
-                return ret;
+                goto done;
             }
 
             /* There are no dependent requests, but we need to remove our
@@ -2096,11 +2101,17 @@ static int preallocate(BlockDriverState *bs,
         ret = bdrv_pwrite(bs->file, (host_offset + cur_bytes) - 1,
                           &data, 1);
         if (ret < 0) {
-            return ret;
+            goto done;
         }
     }
 
-    return 0;
+    ret = 0;
+
+done:
+    if (qemu_in_coroutine()) {
+        qemu_co_mutex_unlock(&s->lock);
+    }
+    return ret;
 }
 
 /* qcow2_refcount_metadata_size:
@@ -2389,10 +2400,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
 
     /* And if we're supposed to preallocate metadata, do that now */
     if (prealloc != PREALLOC_MODE_OFF) {
-        BDRVQcow2State *s = blk_bs(blk)->opaque;
-        qemu_co_mutex_lock(&s->lock);
         ret = preallocate(blk_bs(blk), 0, total_size);
-        qemu_co_mutex_unlock(&s->lock);
         if (ret < 0) {
             error_setg_errno(errp, -ret, "Could not preallocate metadata");
             goto out;
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 11/16] block/qcow2: Metadata preallocation for truncate
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (9 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 10/16] block/qcow2: Lock s->lock in preallocate() Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 12/16] block/qcow2: Add qcow2_refcount_area() Max Reitz
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

We can support PREALLOC_MODE_METADATA by invoking preallocate() in
qcow2_truncate().

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 block/qcow2.c | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/block/qcow2.c b/block/qcow2.c
index a8223a0..2de27c3 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2616,10 +2616,11 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
                           PreallocMode prealloc, Error **errp)
 {
     BDRVQcow2State *s = bs->opaque;
+    uint64_t old_length;
     int64_t new_l1_size;
     int ret;
 
-    if (prealloc != PREALLOC_MODE_OFF) {
+    if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_METADATA) {
         error_setg(errp, "Unsupported preallocation mode '%s'",
                    PreallocMode_lookup[prealloc]);
         return -ENOTSUP;
@@ -2636,8 +2637,10 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
         return -ENOTSUP;
     }
 
+    old_length = bs->total_sectors * 512;
+
     /* shrinking is currently not supported */
-    if (offset < bs->total_sectors * 512) {
+    if (offset < old_length) {
         error_setg(errp, "qcow2 doesn't support shrinking images yet");
         return -ENOTSUP;
     }
@@ -2649,6 +2652,23 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
         return ret;
     }
 
+    switch (prealloc) {
+    case PREALLOC_MODE_OFF:
+        break;
+
+    case PREALLOC_MODE_METADATA:
+        ret = preallocate(bs, old_length, offset);
+        if (ret < 0) {
+            error_setg_errno(errp, -ret, "Preallocation failed, image may now "
+                             "occupy more space than necessary");
+            return ret;
+        }
+        break;
+
+    default:
+        g_assert_not_reached();
+    }
+
     /* write updated header.size */
     offset = cpu_to_be64(offset);
     ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size),
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 12/16] block/qcow2: Add qcow2_refcount_area()
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (10 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 11/16] block/qcow2: Metadata preallocation for truncate Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-31 10:36   ` Stefan Hajnoczi
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 13/16] block/qcow2: Rename "fail_block" to just "fail" Max Reitz
                   ` (3 subsequent siblings)
  15 siblings, 1 reply; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

This function creates a collection of self-describing refcount
structures (including a new refcount table) at the end of a qcow2 image
file. Optionally, these structures can also describe a number of
additional clusters beyond themselves; this will be important for
preallocated truncation, which will place the data clusters and L2
tables there.

For now, we can use this function to replace the part of
alloc_refcount_block() that grows the refcount table (from which it is
actually derived).

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 block/qcow2.h              |   4 +
 block/qcow2-refcount.c     | 267 +++++++++++++++++++++++++++++++--------------
 block/qcow2.c              |  20 +++-
 tests/qemu-iotests/044.out |   2 +-
 4 files changed, 204 insertions(+), 89 deletions(-)

diff --git a/block/qcow2.h b/block/qcow2.h
index 1801dc3..c216bf4 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -485,6 +485,10 @@ static inline uint64_t refcount_diff(uint64_t r1, uint64_t r2)
 int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
                   int64_t sector_num, int nb_sectors);
 
+int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
+                                     int refcount_order, bool generous_increase,
+                                     uint64_t *refblock_count);
+
 int qcow2_mark_dirty(BlockDriverState *bs);
 int qcow2_mark_corrupt(BlockDriverState *bs);
 int qcow2_mark_consistent(BlockDriverState *bs);
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 84e0cee..0872c25 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -34,6 +34,10 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
 static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
                             int64_t offset, int64_t length, uint64_t addend,
                             bool decrease, enum qcow2_discard_type type);
+static int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t offset,
+                                   uint64_t additional_clusters,
+                                   bool exact_size, int new_refblock_index,
+                                   uint64_t new_refblock_offset);
 
 static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index);
 static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index);
@@ -281,25 +285,6 @@ int qcow2_get_refcount(BlockDriverState *bs, int64_t cluster_index,
     return 0;
 }
 
-/*
- * Rounds the refcount table size up to avoid growing the table for each single
- * refcount block that is allocated.
- */
-static unsigned int next_refcount_table_size(BDRVQcow2State *s,
-    unsigned int min_size)
-{
-    unsigned int min_clusters = (min_size >> (s->cluster_bits - 3)) + 1;
-    unsigned int refcount_table_clusters =
-        MAX(1, s->refcount_table_size >> (s->cluster_bits - 3));
-
-    while (min_clusters > refcount_table_clusters) {
-        refcount_table_clusters = (refcount_table_clusters * 3 + 1) / 2;
-    }
-
-    return refcount_table_clusters << (s->cluster_bits - 3);
-}
-
-
 /* Checks if two offsets are described by the same refcount block */
 static int in_same_refcount_block(BDRVQcow2State *s, uint64_t offset_a,
     uint64_t offset_b)
@@ -321,7 +306,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
 {
     BDRVQcow2State *s = bs->opaque;
     unsigned int refcount_table_index;
-    int ret;
+    int64_t ret;
 
     BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC);
 
@@ -490,74 +475,201 @@ static int alloc_refcount_block(BlockDriverState *bs,
                                             (new_block >> s->cluster_bits) + 1),
                                         s->refcount_block_size);
 
-    if (blocks_used > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) {
-        return -EFBIG;
+    /* Create the new refcount table and blocks */
+    uint64_t meta_offset = (blocks_used * s->refcount_block_size) *
+        s->cluster_size;
+
+    ret = qcow2_refcount_area(bs, meta_offset, 0, false,
+                              refcount_table_index, new_block);
+    if (ret < 0) {
+        return ret;
     }
 
-    /* And now we need at least one block more for the new metadata */
-    uint64_t table_size = next_refcount_table_size(s, blocks_used + 1);
-    uint64_t last_table_size;
-    uint64_t blocks_clusters;
-    do {
-        uint64_t table_clusters =
-            size_to_clusters(s, table_size * sizeof(uint64_t));
-        blocks_clusters = 1 +
-            DIV_ROUND_UP(table_clusters, s->refcount_block_size);
-        uint64_t meta_clusters = table_clusters + blocks_clusters;
+    ret = load_refcount_block(bs, new_block, refcount_block);
+    if (ret < 0) {
+        return ret;
+    }
+
+    /* If we were trying to do the initial refcount update for some cluster
+     * allocation, we might have used the same clusters to store newly
+     * allocated metadata. Make the caller search some new space. */
+    return -EAGAIN;
+
+fail_block:
+    if (*refcount_block != NULL) {
+        qcow2_cache_put(bs, s->refcount_block_cache, refcount_block);
+    }
+    return ret;
+}
+
+/*
+ * Starting at @start_offset, this function creates new self-covering refcount
+ * structures: A new refcount table and refcount blocks which cover all of
+ * themselves, and a number of @additional_clusters beyond their end.
+ * @start_offset must be at the end of the image file, that is, there must be
+ * only empty space beyond it.
+ * If @exact_size is false, the refcount table will have 50 % more entries than
+ * necessary so it will not need to grow again soon.
+ * If @new_refblock_offset is not zero, it contains the offset of a refcount
+ * block that should be entered into the new refcount table at index
+ * @new_refblock_index.
+ *
+ * Returns: The offset after the new refcount structures (i.e. where the
+ *          @additional_clusters may be placed) on success, -errno on error.
+ */
+static int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset,
+                                   uint64_t additional_clusters,
+                                   bool exact_size, int new_refblock_index,
+                                   uint64_t new_refblock_offset)
+{
+    BDRVQcow2State *s = bs->opaque;
+    uint64_t total_refblock_count_u64, additional_refblock_count;
+    int total_refblock_count, table_size, area_reftable_index, table_clusters;
+    int i;
+    uint64_t table_offset, block_offset, end_offset;
+    int ret;
+    uint64_t *new_table;
 
-        last_table_size = table_size;
-        table_size = next_refcount_table_size(s, blocks_used +
-            DIV_ROUND_UP(meta_clusters, s->refcount_block_size));
+    assert(!(start_offset % s->cluster_size));
 
-    } while (last_table_size != table_size);
+    qcow2_refcount_metadata_size(start_offset / s->cluster_size +
+                                 additional_clusters,
+                                 s->cluster_size, s->refcount_order,
+                                 !exact_size, &total_refblock_count_u64);
+    if (total_refblock_count_u64 > QCOW_MAX_REFTABLE_SIZE) {
+        return -EFBIG;
+    }
+    total_refblock_count = total_refblock_count_u64;
 
-#ifdef DEBUG_ALLOC2
-    fprintf(stderr, "qcow2: Grow refcount table %" PRId32 " => %" PRId64 "\n",
-        s->refcount_table_size, table_size);
-#endif
+    /* Index in the refcount table of the first refcount block to cover the area
+     * of refcount structures we are about to create; we know that
+     * @total_refblock_count can cover @start_offset, so this will definitely
+     * fit into an int. */
+    area_reftable_index = (start_offset / s->cluster_size) /
+                          s->refcount_block_size;
 
-    /* Create the new refcount table and blocks */
-    uint64_t meta_offset = (blocks_used * s->refcount_block_size) *
-        s->cluster_size;
-    uint64_t table_offset = meta_offset + blocks_clusters * s->cluster_size;
-    uint64_t *new_table = g_try_new0(uint64_t, table_size);
-    void *new_blocks = g_try_malloc0(blocks_clusters * s->cluster_size);
+    if (exact_size) {
+        table_size = total_refblock_count;
+    } else {
+        table_size = total_refblock_count +
+                     DIV_ROUND_UP(total_refblock_count, 2);
+    }
+    /* The qcow2 file can only store the reftable size in number of clusters */
+    table_size = ROUND_UP(table_size, s->cluster_size / sizeof(uint64_t));
+    table_clusters = (table_size * sizeof(uint64_t)) / s->cluster_size;
 
-    assert(table_size > 0 && blocks_clusters > 0);
-    if (new_table == NULL || new_blocks == NULL) {
+    if (table_size > QCOW_MAX_REFTABLE_SIZE) {
+        return -EFBIG;
+    }
+
+    new_table = g_try_new0(uint64_t, table_size);
+
+    assert(table_size > 0);
+    if (new_table == NULL) {
         ret = -ENOMEM;
-        goto fail_table;
+        goto fail;
     }
 
     /* Fill the new refcount table */
-    memcpy(new_table, s->refcount_table,
-        s->refcount_table_size * sizeof(uint64_t));
-    new_table[refcount_table_index] = new_block;
+    if (table_size > s->max_refcount_table_index) {
+        /* We're actually growing the reftable */
+        memcpy(new_table, s->refcount_table,
+               (s->max_refcount_table_index + 1) * sizeof(uint64_t));
+    } else {
+        /* Improbable case: We're shrinking the reftable. However, the caller
+         * has assured us that there is only empty space beyond @start_offset,
+         * so we can simply drop all of the refblocks that won't fit into the
+         * new reftable. */
+        memcpy(new_table, s->refcount_table, table_size * sizeof(uint64_t));
+    }
 
-    int i;
-    for (i = 0; i < blocks_clusters; i++) {
-        new_table[blocks_used + i] = meta_offset + (i * s->cluster_size);
+    if (new_refblock_offset) {
+        assert(new_refblock_index < total_refblock_count);
+        new_table[new_refblock_index] = new_refblock_offset;
     }
 
-    /* Fill the refcount blocks */
-    uint64_t table_clusters = size_to_clusters(s, table_size * sizeof(uint64_t));
-    int block = 0;
-    for (i = 0; i < table_clusters + blocks_clusters; i++) {
-        s->set_refcount(new_blocks, block++, 1);
+    /* Count how many new refblocks we have to create */
+    additional_refblock_count = 0;
+    for (i = area_reftable_index; i < total_refblock_count; i++) {
+        if (!new_table[i]) {
+            additional_refblock_count++;
+        }
     }
 
+    table_offset = start_offset + additional_refblock_count * s->cluster_size;
+    end_offset = table_offset + table_clusters * s->cluster_size;
+
+    /* Fill the refcount blocks, and create new ones, if necessary */
+    block_offset = start_offset;
+    for (i = area_reftable_index; i < total_refblock_count; i++) {
+        void *refblock_data;
+        uint64_t first_offset_covered;
+
+        /* Reuse an existing refblock if possible, create a new one otherwise */
+        if (new_table[i]) {
+            ret = qcow2_cache_get(bs, s->refcount_block_cache, new_table[i],
+                                  &refblock_data);
+            if (ret < 0) {
+                goto fail;
+            }
+        } else {
+            ret = qcow2_cache_get_empty(bs, s->refcount_block_cache,
+                                        block_offset, &refblock_data);
+            if (ret < 0) {
+                goto fail;
+            }
+            memset(refblock_data, 0, s->cluster_size);
+            qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache,
+                                         refblock_data);
+
+            new_table[i] = block_offset;
+            block_offset += s->cluster_size;
+        }
+
+        /* First host offset covered by this refblock */
+        first_offset_covered = (uint64_t)i * s->refcount_block_size *
+                               s->cluster_size;
+        if (first_offset_covered < end_offset) {
+            int j, end_index;
+
+            /* Set the refcount of all of the new refcount structures to 1 */
+
+            if (first_offset_covered < start_offset) {
+                assert(i == area_reftable_index);
+                j = (start_offset - first_offset_covered) / s->cluster_size;
+                assert(j < s->refcount_block_size);
+            } else {
+                j = 0;
+            }
+
+            end_index = MIN((end_offset - first_offset_covered) /
+                            s->cluster_size,
+                            s->refcount_block_size);
+
+            for (; j < end_index; j++) {
+                /* The caller guaranteed us this space would be empty */
+                assert(s->get_refcount(refblock_data, j) == 0);
+                s->set_refcount(refblock_data, j, 1);
+            }
+
+            qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache,
+                                         refblock_data);
+        }
+
+        qcow2_cache_put(bs, s->refcount_block_cache, &refblock_data);
+    }
+
+    assert(block_offset == table_offset);
+
     /* Write refcount blocks to disk */
     BLKDBG_EVENT(bs->file, BLKDBG_REFBLOCK_ALLOC_WRITE_BLOCKS);
-    ret = bdrv_pwrite_sync(bs->file, meta_offset, new_blocks,
-        blocks_clusters * s->cluster_size);
-    g_free(new_blocks);
-    new_blocks = NULL;
+    ret = qcow2_cache_flush(bs, s->refcount_block_cache);
     if (ret < 0) {
-        goto fail_table;
+        goto fail;
     }
 
     /* Write refcount table to disk */
-    for(i = 0; i < table_size; i++) {
+    for (i = 0; i < total_refblock_count; i++) {
         cpu_to_be64s(&new_table[i]);
     }
 
@@ -565,10 +677,10 @@ static int alloc_refcount_block(BlockDriverState *bs,
     ret = bdrv_pwrite_sync(bs->file, table_offset, new_table,
         table_size * sizeof(uint64_t));
     if (ret < 0) {
-        goto fail_table;
+        goto fail;
     }
 
-    for(i = 0; i < table_size; i++) {
+    for (i = 0; i < total_refblock_count; i++) {
         be64_to_cpus(&new_table[i]);
     }
 
@@ -584,7 +696,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
                            offsetof(QCowHeader, refcount_table_offset),
                            &data, sizeof(data));
     if (ret < 0) {
-        goto fail_table;
+        goto fail;
     }
 
     /* And switch it in memory */
@@ -601,23 +713,10 @@ static int alloc_refcount_block(BlockDriverState *bs,
     qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t),
                         QCOW2_DISCARD_OTHER);
 
-    ret = load_refcount_block(bs, new_block, refcount_block);
-    if (ret < 0) {
-        return ret;
-    }
-
-    /* If we were trying to do the initial refcount update for some cluster
-     * allocation, we might have used the same clusters to store newly
-     * allocated metadata. Make the caller search some new space. */
-    return -EAGAIN;
+    return end_offset;
 
-fail_table:
-    g_free(new_blocks);
+fail:
     g_free(new_table);
-fail_block:
-    if (*refcount_block != NULL) {
-        qcow2_cache_put(bs, s->refcount_block_cache, refcount_block);
-    }
     return ret;
 }
 
diff --git a/block/qcow2.c b/block/qcow2.c
index 2de27c3..86497c0 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2118,12 +2118,14 @@ done:
  * @clusters: number of clusters to refcount (including data and L1/L2 tables)
  * @cluster_size: size of a cluster, in bytes
  * @refcount_order: refcount bits power-of-2 exponent
+ * @generous_increase: allow for the refcount table to be 1.5x as large as it
+ *                     needs to be
  *
  * Returns: Number of bytes required for refcount blocks and table metadata.
  */
-static int64_t qcow2_refcount_metadata_size(int64_t clusters,
-                                            size_t cluster_size,
-                                            int refcount_order)
+int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
+                                     int refcount_order, bool generous_increase,
+                                     uint64_t *refblock_count)
 {
     /*
      * Every host cluster is reference-counted, including metadata (even
@@ -2146,8 +2148,18 @@ static int64_t qcow2_refcount_metadata_size(int64_t clusters,
         blocks = DIV_ROUND_UP(clusters + table + blocks, refcounts_per_block);
         table = DIV_ROUND_UP(blocks, blocks_per_table_cluster);
         n = clusters + blocks + table;
+
+        if (n == last && generous_increase) {
+            clusters += DIV_ROUND_UP(table, 2);
+            n = 0; /* force another loop */
+            generous_increase = false;
+        }
     } while (n != last);
 
+    if (refblock_count) {
+        *refblock_count = blocks;
+    }
+
     return (blocks + table) * cluster_size;
 }
 
@@ -2184,7 +2196,7 @@ static int64_t qcow2_calc_prealloc_size(int64_t total_size,
     /* total size of refcount table and blocks */
     meta_size += qcow2_refcount_metadata_size(
             (meta_size + aligned_total_size) / cluster_size,
-            cluster_size, refcount_order);
+            cluster_size, refcount_order, false, NULL);
 
     return meta_size + aligned_total_size;
 }
diff --git a/tests/qemu-iotests/044.out b/tests/qemu-iotests/044.out
index 4789a53..703cf3d 100644
--- a/tests/qemu-iotests/044.out
+++ b/tests/qemu-iotests/044.out
@@ -1,6 +1,6 @@
 No errors were found on the image.
 7292415/33554432 = 21.73% allocated, 0.00% fragmented, 0.00% compressed clusters
-Image end offset: 4296152064
+Image end offset: 4296217088
 .
 ----------------------------------------------------------------------
 Ran 1 tests
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 13/16] block/qcow2: Rename "fail_block" to just "fail"
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (11 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 12/16] block/qcow2: Add qcow2_refcount_area() Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-31 10:36   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 14/16] block/qcow2: falloc/full preallocating growth Max Reitz
                   ` (2 subsequent siblings)
  15 siblings, 1 reply; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

Now alloc_refcount_block() only contains a single fail label, so it
makes more sense to just name it "fail" instead of "fail_block".

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 block/qcow2-refcount.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 0872c25..9d109e9 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -381,7 +381,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
         ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block,
                                     refcount_block);
         if (ret < 0) {
-            goto fail_block;
+            goto fail;
         }
 
         memset(*refcount_block, 0, s->cluster_size);
@@ -396,12 +396,12 @@ static int alloc_refcount_block(BlockDriverState *bs,
         ret = update_refcount(bs, new_block, s->cluster_size, 1, false,
                               QCOW2_DISCARD_NEVER);
         if (ret < 0) {
-            goto fail_block;
+            goto fail;
         }
 
         ret = qcow2_cache_flush(bs, s->refcount_block_cache);
         if (ret < 0) {
-            goto fail_block;
+            goto fail;
         }
 
         /* Initialize the new refcount block only after updating its refcount,
@@ -409,7 +409,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
         ret = qcow2_cache_get_empty(bs, s->refcount_block_cache, new_block,
                                     refcount_block);
         if (ret < 0) {
-            goto fail_block;
+            goto fail;
         }
 
         memset(*refcount_block, 0, s->cluster_size);
@@ -420,7 +420,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
     qcow2_cache_entry_mark_dirty(bs, s->refcount_block_cache, *refcount_block);
     ret = qcow2_cache_flush(bs, s->refcount_block_cache);
     if (ret < 0) {
-        goto fail_block;
+        goto fail;
     }
 
     /* If the refcount table is big enough, just hook the block up there */
@@ -431,7 +431,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
             s->refcount_table_offset + refcount_table_index * sizeof(uint64_t),
             &data64, sizeof(data64));
         if (ret < 0) {
-            goto fail_block;
+            goto fail;
         }
 
         s->refcount_table[refcount_table_index] = new_block;
@@ -495,7 +495,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
      * allocated metadata. Make the caller search some new space. */
     return -EAGAIN;
 
-fail_block:
+fail:
     if (*refcount_block != NULL) {
         qcow2_cache_put(bs, s->refcount_block_cache, refcount_block);
     }
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 14/16] block/qcow2: falloc/full preallocating growth
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (12 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 13/16] block/qcow2: Rename "fail_block" to just "fail" Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-31 10:41   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 15/16] iotests: Add preallocated resize test for raw Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 16/16] iotests: Add preallocated growth test for qcow2 Max Reitz
  15 siblings, 1 reply; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

Implement the preallocation modes falloc and full for growing qcow2
images.

Signed-off-by: Max Reitz <mreitz@redhat.com>
---
 block/qcow2.h          |   5 +++
 block/qcow2-refcount.c |  12 ++----
 block/qcow2.c          | 100 ++++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 108 insertions(+), 9 deletions(-)

diff --git a/block/qcow2.h b/block/qcow2.h
index c216bf4..cf33e4e 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -509,6 +509,11 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index,
                                   uint64_t addend, bool decrease,
                                   enum qcow2_discard_type type);
 
+int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t offset,
+                            uint64_t additional_clusters, bool exact_size,
+                            int new_refblock_index,
+                            uint64_t new_refblock_offset);
+
 int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size);
 int64_t qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
                                 int64_t nb_clusters);
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
index 9d109e9..fcaa7ac 100644
--- a/block/qcow2-refcount.c
+++ b/block/qcow2-refcount.c
@@ -34,10 +34,6 @@ static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
 static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
                             int64_t offset, int64_t length, uint64_t addend,
                             bool decrease, enum qcow2_discard_type type);
-static int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t offset,
-                                   uint64_t additional_clusters,
-                                   bool exact_size, int new_refblock_index,
-                                   uint64_t new_refblock_offset);
 
 static uint64_t get_refcount_ro0(const void *refcount_array, uint64_t index);
 static uint64_t get_refcount_ro1(const void *refcount_array, uint64_t index);
@@ -517,10 +513,10 @@ fail:
  * Returns: The offset after the new refcount structures (i.e. where the
  *          @additional_clusters may be placed) on success, -errno on error.
  */
-static int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset,
-                                   uint64_t additional_clusters,
-                                   bool exact_size, int new_refblock_index,
-                                   uint64_t new_refblock_offset)
+int64_t qcow2_refcount_area(BlockDriverState *bs, uint64_t start_offset,
+                            uint64_t additional_clusters, bool exact_size,
+                            int new_refblock_index,
+                            uint64_t new_refblock_offset)
 {
     BDRVQcow2State *s = bs->opaque;
     uint64_t total_refblock_count_u64, additional_refblock_count;
diff --git a/block/qcow2.c b/block/qcow2.c
index 86497c0..854f5e5 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2632,7 +2632,9 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
     int64_t new_l1_size;
     int ret;
 
-    if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_METADATA) {
+    if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_METADATA &&
+        prealloc != PREALLOC_MODE_FALLOC && prealloc != PREALLOC_MODE_FULL)
+    {
         error_setg(errp, "Unsupported preallocation mode '%s'",
                    PreallocMode_lookup[prealloc]);
         return -ENOTSUP;
@@ -2677,6 +2679,102 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
         }
         break;
 
+    case PREALLOC_MODE_FALLOC:
+    case PREALLOC_MODE_FULL:
+    {
+        int64_t allocation_start, host_offset, guest_offset;
+        int64_t clusters_allocated;
+        int64_t old_file_size, new_file_size;
+        uint64_t nb_new_data_clusters, nb_new_l2_tables;
+
+        old_file_size = bdrv_getlength(bs->file->bs);
+        if (old_file_size < 0) {
+            error_setg_errno(errp, -old_file_size,
+                             "Failed to inquire current file length");
+            return ret;
+        }
+
+        nb_new_data_clusters = DIV_ROUND_UP(offset - old_length,
+                                            s->cluster_size);
+
+        /* This is an overestimation; we will not actually allocate space for
+         * these in the file but just make sure the new refcount structures are
+         * able to cover them so we will not have to allocate new refblocks
+         * while entering the data blocks in the potentially new L2 tables.
+         * (We do not actually care where the L2 tables are placed. Maybe they
+         *  are already allocated or they can be placed somewhere before
+         *  @old_file_size. It does not matter because they will be fully
+         *  allocated automatically, so they do not need to be covered by the
+         *  preallocation. All that matters is that we will not have to allocate
+         *  new refcount structures for them.) */
+        nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters,
+                                        s->cluster_size / sizeof(uint64_t));
+        /* The cluster range may not be aligned to L2 boundaries, so add one L2
+         * table for a potential head/tail */
+        nb_new_l2_tables++;
+
+        allocation_start = qcow2_refcount_area(bs, old_file_size,
+                                               nb_new_data_clusters +
+                                               nb_new_l2_tables,
+                                               true, 0, 0);
+        if (allocation_start < 0) {
+            error_setg_errno(errp, -allocation_start,
+                             "Failed to resize refcount structures");
+            return -allocation_start;
+        }
+
+        clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start,
+                                                     nb_new_data_clusters);
+        if (clusters_allocated < 0) {
+            error_setg_errno(errp, -clusters_allocated,
+                             "Failed to allocate data clusters");
+            return -clusters_allocated;
+        }
+
+        assert(clusters_allocated == nb_new_data_clusters);
+
+        /* Allocate the data area */
+        new_file_size = allocation_start +
+                        nb_new_data_clusters * s->cluster_size;
+        ret = bdrv_truncate(bs->file, new_file_size, prealloc, errp);
+        if (ret < 0) {
+            error_prepend(errp, "Failed to resize underlying file: ");
+            qcow2_free_clusters(bs, allocation_start,
+                                nb_new_data_clusters * s->cluster_size,
+                                QCOW2_DISCARD_OTHER);
+            return ret;
+        }
+
+        /* Create the necessary L2 entries */
+        host_offset = allocation_start;
+        guest_offset = old_length;
+        while (nb_new_data_clusters) {
+            int64_t guest_cluster = guest_offset >> s->cluster_bits;
+            int64_t nb_clusters = MIN(nb_new_data_clusters,
+                                      s->l2_size - guest_cluster % s->l2_size);
+            QCowL2Meta allocation = {
+                .offset       = guest_offset,
+                .alloc_offset = host_offset,
+                .nb_clusters  = nb_clusters,
+            };
+            qemu_co_queue_init(&allocation.dependent_requests);
+
+            ret = qcow2_alloc_cluster_link_l2(bs, &allocation);
+            if (ret < 0) {
+                error_setg_errno(errp, -ret, "Failed to update L2 tables");
+                qcow2_free_clusters(bs, host_offset,
+                                    nb_new_data_clusters * s->cluster_size,
+                                    QCOW2_DISCARD_OTHER);
+                return ret;
+            }
+
+            guest_offset += nb_clusters * s->cluster_size;
+            host_offset += nb_clusters * s->cluster_size;
+            nb_new_data_clusters -= nb_clusters;
+        }
+        break;
+    }
+
     default:
         g_assert_not_reached();
     }
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 15/16] iotests: Add preallocated resize test for raw
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (13 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 14/16] block/qcow2: falloc/full preallocating growth Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 16/16] iotests: Add preallocated growth test for qcow2 Max Reitz
  15 siblings, 0 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/qemu-iotests/106     | 92 ++++++++++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/106.out | 50 +++++++++++++++++++++++++
 tests/qemu-iotests/group   |  1 +
 3 files changed, 143 insertions(+)
 create mode 100755 tests/qemu-iotests/106
 create mode 100644 tests/qemu-iotests/106.out

diff --git a/tests/qemu-iotests/106 b/tests/qemu-iotests/106
new file mode 100755
index 0000000..3264957
--- /dev/null
+++ b/tests/qemu-iotests/106
@@ -0,0 +1,92 @@
+#!/bin/bash
+#
+# Test preallocated resize of raw images
+#
+# Copyright (C) 2017 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mreitz@redhat.com
+
+seq=$(basename $0)
+echo "QA output created by $seq"
+
+here=$PWD
+status=1	# failure is the default!
+
+_cleanup()
+{
+	_cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment and filters
+. ./common.rc
+. ./common.filter
+
+_supported_fmt raw
+_supported_proto file
+_supported_os Linux
+
+# in kB
+CREATION_SIZE=128
+GROWTH_SIZE=256
+
+echo '=== Testing image growth ==='
+
+for create_mode in off falloc full; do
+    for growth_mode in off falloc full; do
+        echo
+        echo "--- create_mode=$create_mode growth_mode=$growth_mode ---"
+
+        IMGOPTS="preallocation=$create_mode" _make_test_img ${CREATION_SIZE}K
+        $QEMU_IMG resize -f "$IMGFMT" --preallocation=$growth_mode "$TEST_IMG" +${GROWTH_SIZE}K
+
+        expected_size=0
+        if [ $create_mode != off ]; then
+            expected_size=$CREATION_SIZE
+        fi
+        if [ $growth_mode != off ]; then
+            expected_size=$((expected_size + $GROWTH_SIZE))
+        fi
+
+        actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size')
+        actual_size=$(echo "$actual_size" | sed -e 's/^[^0-9]*\([0-9]\+\).*$/\1/')
+
+        # The actual size may exceed the expected size, depending on the file
+        # system. Therefore we just test that the actual size is at least what
+        # we expect.
+        if [ $actual_size -lt $expected_size ]; then
+            echo "ERROR: Image should have at least ${expected_size}K, but has ${actual_size}K"
+        fi
+    done
+done
+
+echo
+echo '=== Testing image shrinking ==='
+
+# None of this should work except for "off", because other modes cannot be used
+# for shrinking
+for growth_mode in falloc full off; do
+    echo
+    echo "--- growth_mode=$growth_mode ---"
+    $QEMU_IMG resize -f "$IMGFMT" --preallocation=$growth_mode "$TEST_IMG" -${GROWTH_SIZE}K
+done
+
+# success, all done
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/106.out b/tests/qemu-iotests/106.out
new file mode 100644
index 0000000..0a42312
--- /dev/null
+++ b/tests/qemu-iotests/106.out
@@ -0,0 +1,50 @@
+QA output created by 106
+=== Testing image growth ===
+
+--- create_mode=off growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 preallocation=off
+Image resized.
+
+--- create_mode=off growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 preallocation=off
+Image resized.
+
+--- create_mode=off growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 preallocation=off
+Image resized.
+
+--- create_mode=falloc growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 preallocation=falloc
+Image resized.
+
+--- create_mode=falloc growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 preallocation=falloc
+Image resized.
+
+--- create_mode=falloc growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 preallocation=falloc
+Image resized.
+
+--- create_mode=full growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 preallocation=full
+Image resized.
+
+--- create_mode=full growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 preallocation=full
+Image resized.
+
+--- create_mode=full growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=131072 preallocation=full
+Image resized.
+
+=== Testing image shrinking ===
+
+--- growth_mode=falloc ---
+qemu-img: Preallocation can only be used for growing images
+
+--- growth_mode=full ---
+qemu-img: Preallocation can only be used for growing images
+
+--- growth_mode=off ---
+Image resized.
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index ae2ef21..9846e03 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -112,6 +112,7 @@
 103 rw auto quick
 104 rw auto
 105 rw auto quick
+106 rw auto quick
 107 rw auto quick
 108 rw auto quick
 109 rw auto
-- 
2.9.4

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

* [Qemu-devel] [PATCH v3 16/16] iotests: Add preallocated growth test for qcow2
  2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
                   ` (14 preceding siblings ...)
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 15/16] iotests: Add preallocated resize test for raw Max Reitz
@ 2017-05-26 16:55 ` Max Reitz
  15 siblings, 0 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-26 16:55 UTC (permalink / raw)
  To: qemu-block; +Cc: qemu-devel, Max Reitz, Kevin Wolf, Stefan Hajnoczi

Signed-off-by: Max Reitz <mreitz@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/qemu-iotests/125     | 130 +++++++++++++++
 tests/qemu-iotests/125.out | 386 +++++++++++++++++++++++++++++++++++++++++++++
 tests/qemu-iotests/group   |   1 +
 3 files changed, 517 insertions(+)
 create mode 100755 tests/qemu-iotests/125
 create mode 100644 tests/qemu-iotests/125.out

diff --git a/tests/qemu-iotests/125 b/tests/qemu-iotests/125
new file mode 100755
index 0000000..9424313
--- /dev/null
+++ b/tests/qemu-iotests/125
@@ -0,0 +1,130 @@
+#!/bin/bash
+#
+# Test preallocated growth of qcow2 images
+#
+# Copyright (C) 2017 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# creator
+owner=mreitz@redhat.com
+
+seq=$(basename $0)
+echo "QA output created by $seq"
+
+here=$PWD
+status=1	# failure is the default!
+
+_cleanup()
+{
+	_cleanup_test_img
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+get_image_size_on_host()
+{
+    $QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep "disk size" \
+        | sed -e 's/^[^0-9]*\([0-9]\+\).*$/\1/'
+}
+
+# get standard environment and filters
+. ./common.rc
+. ./common.filter
+
+_supported_fmt qcow2
+_supported_proto file
+_supported_os Linux
+
+if [ -z "$TEST_IMG_FILE" ]; then
+    TEST_IMG_FILE=$TEST_IMG
+fi
+
+# Generally, we create some image with or without existing preallocation and
+# then resize it. Then we write some data into the image and verify that its
+# size does not change if we have used preallocation.
+
+# With a cluster size of 512 B, one L2 table covers 64 * 512 B = 32 kB.
+# One cluster of the L1 table covers 64 * 32 kB = 2 MB.
+# There are multiple cases we want to test:
+# (1) Grow an image without having to allocate a new L2 table.
+# (2) Grow an image, having to allocate a new L2 table.
+# (3) Grow an image, having to grow the L1 table.
+# Therefore, we create an image that is 48 kB below 2 MB. Then:
+# (1) We resize it to 2 MB - 32 kB. (+ 16 kB)
+# (2) We resize it to 2 MB.         (+ 48 kB)
+# (3) We resize it to 2 MB + 32 kB. (+ 80 kB)
+
+# in B
+CREATION_SIZE=$((2 * 1024 * 1024 - 48 * 1024))
+
+# in kB
+for GROWTH_SIZE in 16 48 80; do
+    for create_mode in off metadata falloc full; do
+        for growth_mode in off metadata falloc full; do
+            echo "--- growth_size=$GROWTH_SIZE create_mode=$create_mode growth_mode=$growth_mode ---"
+
+            IMGOPTS="preallocation=$create_mode,cluster_size=512" _make_test_img ${CREATION_SIZE}
+            $QEMU_IMG resize -f "$IMGFMT" --preallocation=$growth_mode "$TEST_IMG" +${GROWTH_SIZE}K
+
+            host_size_0=$(get_image_size_on_host)
+            file_length_0=$(stat -c '%s' "$TEST_IMG_FILE")
+
+            $QEMU_IO -c "write 0 $CREATION_SIZE" "$TEST_IMG" | _filter_qemu_io
+
+            host_size_1=$(get_image_size_on_host)
+            file_length_1=$(stat -c '%s' "$TEST_IMG_FILE")
+
+            $QEMU_IO -c "write $CREATION_SIZE ${GROWTH_SIZE}K" "$TEST_IMG" | _filter_qemu_io
+
+            host_size_2=$(get_image_size_on_host)
+            file_length_2=$(stat -c '%s' "$TEST_IMG_FILE")
+
+            # Test creation preallocation: Compare #0 against #1
+            if [ $create_mode != off ]; then
+                # The image length should not have grown
+                if [ $file_length_1 -gt $file_length_0 ]; then
+                    echo "ERROR (create): Image length has grown from $file_length_0 to $file_length_1"
+                fi
+                if [ $create_mode != metadata ]; then
+                    # The host size should not have grown either
+                    if [ $host_size_1 -gt $host_size_0 ]; then
+                        echo "ERROR (create): Host size has grown from $host_size_0 to $host_size_1"
+                    fi
+                fi
+            fi
+
+            # Test resize preallocation: Compare #2 against #1
+            if [ $growth_mode != off ]; then
+                # The image length should not have grown
+                if [ $file_length_2 -gt $file_length_1 ]; then
+                    echo "ERROR (grow): Image length has grown from $file_length_1 to $file_length_2"
+                fi
+                if [ $create_mode != metadata ]; then
+                    # The host size should not have grown either
+                    if [ $host_size_2 -gt $host_size_1 ]; then
+                        echo "ERROR (grow): Host size has grown from $host_size_1 to $host_size_2"
+                    fi
+                fi
+            fi
+
+            echo
+        done
+    done
+done
+
+# success, all done
+echo '*** done'
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/125.out b/tests/qemu-iotests/125.out
new file mode 100644
index 0000000..3f4d6e3
--- /dev/null
+++ b/tests/qemu-iotests/125.out
@@ -0,0 +1,386 @@
+QA output created by 125
+--- growth_size=16 create_mode=off growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=off
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=off growth_mode=metadata ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=off
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=off growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=off
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=off growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=off
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=metadata growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=metadata
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=metadata growth_mode=metadata ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=metadata
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=metadata growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=metadata
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=metadata growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=metadata
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=falloc growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=falloc
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=falloc growth_mode=metadata ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=falloc
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=falloc growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=falloc
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=falloc growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=falloc
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=full growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=full
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=full growth_mode=metadata ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=full
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=full growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=full
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=16 create_mode=full growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=full
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 16384/16384 bytes at offset 2048000
+16 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=off growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=off
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=off growth_mode=metadata ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=off
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=off growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=off
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=off growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=off
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=metadata growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=metadata
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=metadata growth_mode=metadata ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=metadata
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=metadata growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=metadata
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=metadata growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=metadata
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=falloc growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=falloc
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=falloc growth_mode=metadata ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=falloc
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=falloc growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=falloc
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=falloc growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=falloc
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=full growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=full
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=full growth_mode=metadata ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=full
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=full growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=full
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=48 create_mode=full growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=full
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 49152/49152 bytes at offset 2048000
+48 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=off growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=off
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=off growth_mode=metadata ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=off
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=off growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=off
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=off growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=off
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=metadata growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=metadata
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=metadata growth_mode=metadata ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=metadata
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=metadata growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=metadata
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=metadata growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=metadata
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=falloc growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=falloc
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=falloc growth_mode=metadata ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=falloc
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=falloc growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=falloc
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=falloc growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=falloc
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=full growth_mode=off ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=full
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=full growth_mode=metadata ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=full
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=full growth_mode=falloc ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=full
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+--- growth_size=80 create_mode=full growth_mode=full ---
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=2048000 preallocation=full
+Image resized.
+wrote 2048000/2048000 bytes at offset 0
+1.953 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+wrote 81920/81920 bytes at offset 2048000
+80 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
+
+*** done
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
index 9846e03..2c48b1b 100644
--- a/tests/qemu-iotests/group
+++ b/tests/qemu-iotests/group
@@ -131,6 +131,7 @@
 122 rw auto
 123 rw auto quick
 124 rw auto backing
+125 rw auto
 128 rw auto quick
 129 rw auto quick
 130 rw auto quick
-- 
2.9.4

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

* Re: [Qemu-devel] [PATCH v3 04/16] qemu-img: Expose PreallocMode for resizing
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 04/16] qemu-img: Expose PreallocMode for resizing Max Reitz
@ 2017-05-30 20:57   ` Eric Blake
  2017-05-31 12:12     ` Max Reitz
  2017-05-31 10:09   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  1 sibling, 1 reply; 28+ messages in thread
From: Eric Blake @ 2017-05-30 20:57 UTC (permalink / raw)
  To: Max Reitz, qemu-block; +Cc: Kevin Wolf, qemu-devel, Stefan Hajnoczi

[-- Attachment #1: Type: text/plain, Size: 2002 bytes --]

On 05/26/2017 11:55 AM, Max Reitz wrote:
> Add a --preallocation command line option to qemu-img resize which can
> be used to set the PreallocMode parameter of blk_truncate().
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  qemu-img.c    | 33 ++++++++++++++++++++++++++++++---
>  qemu-img.texi |  7 ++++++-
>  2 files changed, 36 insertions(+), 4 deletions(-)

> @@ -3553,8 +3566,16 @@ static int img_resize(int argc, char **argv)
>          goto out;
>      }
>  
> +    current_size = blk_getlength(blk);
> +    if (current_size < 0) {
> +        error_report("Failed to inquire current image length: %s",
> +                     strerror(-current_size));
> +        ret = -1;
> +        goto out;
> +    }
> +
>      if (relative) {
> -        total_size = blk_getlength(blk) + n * relative;
> +        total_size = current_size + n * relative;

You snuck in a bug fix here (reporting failure, rather than using a
bogus total_size, if querying the size fails).  Please mention that in
the commit message.

> @@ -541,6 +541,11 @@ After using this command to grow a disk image, you must use file system and
>  partitioning tools inside the VM to actually begin using the new space on the
>  device.
>  
> +When growing an image, the @code{--preallocation} option may be used to specify
> +how the additional image area should be allocated on the host.  See the format
> +description in the @code{NOTES} section which values are allowed.  Using this
> +option may result in more data being allocated than necessary.

Should we tone it down a bit by saying 'slightly more data'?  (We'd
rather over-estimate than fall short, but our over-estimation will
probably be < 1% off, and not something drastic like an order of
magnitude off).

With the improved commit message,
Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

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

* Re: [Qemu-devel] [PATCH v3 09/16] block/qcow2: Generalize preallocate()
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 09/16] block/qcow2: Generalize preallocate() Max Reitz
@ 2017-05-30 21:26   ` Eric Blake
  2017-05-31 10:22   ` Stefan Hajnoczi
  1 sibling, 0 replies; 28+ messages in thread
From: Eric Blake @ 2017-05-30 21:26 UTC (permalink / raw)
  To: Max Reitz, qemu-block; +Cc: Kevin Wolf, qemu-devel, Stefan Hajnoczi

[-- Attachment #1: Type: text/plain, Size: 1613 bytes --]

On 05/26/2017 11:55 AM, Max Reitz wrote:
> This patch adds two new parameters to the preallocate() function so we
> will be able to use it not just for preallocating a new image but also
> for preallocated image growth.
> 
> The offset parameter allows the caller to specify a virtual offset from
> which to start preallocating. For newly created images this is always 0,
> but for preallocating growth this will be the old image length.
> 
> The new_length parameter specifies the supposed new length of the image
> (basically the "end offset" for preallocation). During image truncation,
> bdrv_getlength() will return the old image length so we cannot rely on
> its return value then.

bdrv_getlength() is (currently) always sector-aligned (rounding up as
needed).

new_length is passed from qcow2_create2()'s total_size, which in turn
comes from qcow2_create()'s size, which can be user-supplied - but also
appears that we round up to ensure it is always sector-aligned.

Testing: 'qemu-img create -f qcow2 a.img 1' reports "size=1", but
'qemu-img info a.img' reports "virtual size: 512" - so we have a
secondary bug worth fixing later in that we are rounding AFTER what we
report to the user, but I'm not seeing a behavior change in this patch.

> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  block/qcow2.c | 17 ++++++++++++-----
>  1 file changed, 12 insertions(+), 5 deletions(-)
> 

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

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

* Re: [Qemu-devel] [PATCH v3 10/16] block/qcow2: Lock s->lock in preallocate()
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 10/16] block/qcow2: Lock s->lock in preallocate() Max Reitz
@ 2017-05-30 21:28   ` Eric Blake
  2017-05-31 10:24   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
  1 sibling, 0 replies; 28+ messages in thread
From: Eric Blake @ 2017-05-30 21:28 UTC (permalink / raw)
  To: Max Reitz, qemu-block; +Cc: Kevin Wolf, qemu-devel, Stefan Hajnoczi

[-- Attachment #1: Type: text/plain, Size: 711 bytes --]

On 05/26/2017 11:55 AM, Max Reitz wrote:
> preallocate() is and will be called only from places that do not lock

Maybe: "that do not otherwise need to lock"

> s->lock: Currently that is qcow2_create2(), as of a future patch it will
> be called from qcow2_truncate(), too.
> 
> It therefore makes sense to move locking that mutex into preallocate()
> itself.
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  block/qcow2.c | 22 +++++++++++++++-------
>  1 file changed, 15 insertions(+), 7 deletions(-)
> 

Reviewed-by: Eric Blake <eblake@redhat.com>

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH v3 04/16] qemu-img: Expose PreallocMode for resizing
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 04/16] qemu-img: Expose PreallocMode for resizing Max Reitz
  2017-05-30 20:57   ` Eric Blake
@ 2017-05-31 10:09   ` Stefan Hajnoczi
  1 sibling, 0 replies; 28+ messages in thread
From: Stefan Hajnoczi @ 2017-05-31 10:09 UTC (permalink / raw)
  To: Max Reitz; +Cc: qemu-block, Kevin Wolf, qemu-devel, Stefan Hajnoczi

[-- Attachment #1: Type: text/plain, Size: 453 bytes --]

On Fri, May 26, 2017 at 06:55:06PM +0200, Max Reitz wrote:
> Add a --preallocation command line option to qemu-img resize which can
> be used to set the PreallocMode parameter of blk_truncate().
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  qemu-img.c    | 33 ++++++++++++++++++++++++++++++---
>  qemu-img.texi |  7 ++++++-
>  2 files changed, 36 insertions(+), 4 deletions(-)

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [PATCH v3 09/16] block/qcow2: Generalize preallocate()
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 09/16] block/qcow2: Generalize preallocate() Max Reitz
  2017-05-30 21:26   ` Eric Blake
@ 2017-05-31 10:22   ` Stefan Hajnoczi
  1 sibling, 0 replies; 28+ messages in thread
From: Stefan Hajnoczi @ 2017-05-31 10:22 UTC (permalink / raw)
  To: Max Reitz; +Cc: qemu-block, Kevin Wolf, qemu-devel, Stefan Hajnoczi

[-- Attachment #1: Type: text/plain, Size: 926 bytes --]

On Fri, May 26, 2017 at 06:55:11PM +0200, Max Reitz wrote:
> This patch adds two new parameters to the preallocate() function so we
> will be able to use it not just for preallocating a new image but also
> for preallocated image growth.
> 
> The offset parameter allows the caller to specify a virtual offset from
> which to start preallocating. For newly created images this is always 0,
> but for preallocating growth this will be the old image length.
> 
> The new_length parameter specifies the supposed new length of the image
> (basically the "end offset" for preallocation). During image truncation,
> bdrv_getlength() will return the old image length so we cannot rely on
> its return value then.
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  block/qcow2.c | 17 ++++++++++++-----
>  1 file changed, 12 insertions(+), 5 deletions(-)

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH v3 10/16] block/qcow2: Lock s->lock in preallocate()
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 10/16] block/qcow2: Lock s->lock in preallocate() Max Reitz
  2017-05-30 21:28   ` Eric Blake
@ 2017-05-31 10:24   ` Stefan Hajnoczi
  1 sibling, 0 replies; 28+ messages in thread
From: Stefan Hajnoczi @ 2017-05-31 10:24 UTC (permalink / raw)
  To: Max Reitz; +Cc: qemu-block, Kevin Wolf, qemu-devel, Stefan Hajnoczi

[-- Attachment #1: Type: text/plain, Size: 551 bytes --]

On Fri, May 26, 2017 at 06:55:12PM +0200, Max Reitz wrote:
> preallocate() is and will be called only from places that do not lock
> s->lock: Currently that is qcow2_create2(), as of a future patch it will
> be called from qcow2_truncate(), too.
> 
> It therefore makes sense to move locking that mutex into preallocate()
> itself.
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  block/qcow2.c | 22 +++++++++++++++-------
>  1 file changed, 15 insertions(+), 7 deletions(-)

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [PATCH v3 12/16] block/qcow2: Add qcow2_refcount_area()
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 12/16] block/qcow2: Add qcow2_refcount_area() Max Reitz
@ 2017-05-31 10:36   ` Stefan Hajnoczi
  0 siblings, 0 replies; 28+ messages in thread
From: Stefan Hajnoczi @ 2017-05-31 10:36 UTC (permalink / raw)
  To: Max Reitz; +Cc: qemu-block, Kevin Wolf, qemu-devel, Stefan Hajnoczi

[-- Attachment #1: Type: text/plain, Size: 958 bytes --]

On Fri, May 26, 2017 at 06:55:14PM +0200, Max Reitz wrote:
> This function creates a collection of self-describing refcount
> structures (including a new refcount table) at the end of a qcow2 image
> file. Optionally, these structures can also describe a number of
> additional clusters beyond themselves; this will be important for
> preallocated truncation, which will place the data clusters and L2
> tables there.
> 
> For now, we can use this function to replace the part of
> alloc_refcount_block() that grows the refcount table (from which it is
> actually derived).
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  block/qcow2.h              |   4 +
>  block/qcow2-refcount.c     | 267 +++++++++++++++++++++++++++++++--------------
>  block/qcow2.c              |  20 +++-
>  tests/qemu-iotests/044.out |   2 +-
>  4 files changed, 204 insertions(+), 89 deletions(-)

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH v3 13/16] block/qcow2: Rename "fail_block" to just "fail"
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 13/16] block/qcow2: Rename "fail_block" to just "fail" Max Reitz
@ 2017-05-31 10:36   ` Stefan Hajnoczi
  0 siblings, 0 replies; 28+ messages in thread
From: Stefan Hajnoczi @ 2017-05-31 10:36 UTC (permalink / raw)
  To: Max Reitz; +Cc: qemu-block, Kevin Wolf, qemu-devel, Stefan Hajnoczi

[-- Attachment #1: Type: text/plain, Size: 411 bytes --]

On Fri, May 26, 2017 at 06:55:15PM +0200, Max Reitz wrote:
> Now alloc_refcount_block() only contains a single fail label, so it
> makes more sense to just name it "fail" instead of "fail_block".
> 
> Signed-off-by: Max Reitz <mreitz@redhat.com>
> ---
>  block/qcow2-refcount.c | 14 +++++++-------
>  1 file changed, 7 insertions(+), 7 deletions(-)

Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH v3 14/16] block/qcow2: falloc/full preallocating growth
  2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 14/16] block/qcow2: falloc/full preallocating growth Max Reitz
@ 2017-05-31 10:41   ` Stefan Hajnoczi
  2017-05-31 12:19     ` Max Reitz
  0 siblings, 1 reply; 28+ messages in thread
From: Stefan Hajnoczi @ 2017-05-31 10:41 UTC (permalink / raw)
  To: Max Reitz; +Cc: qemu-block, Kevin Wolf, qemu-devel, Stefan Hajnoczi

[-- Attachment #1: Type: text/plain, Size: 4903 bytes --]

On Fri, May 26, 2017 at 06:55:16PM +0200, Max Reitz wrote:
> @@ -2677,6 +2679,102 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
>          }
>          break;
>  
> +    case PREALLOC_MODE_FALLOC:
> +    case PREALLOC_MODE_FULL:
> +    {
> +        int64_t allocation_start, host_offset, guest_offset;
> +        int64_t clusters_allocated;
> +        int64_t old_file_size, new_file_size;
> +        uint64_t nb_new_data_clusters, nb_new_l2_tables;
> +
> +        old_file_size = bdrv_getlength(bs->file->bs);
> +        if (old_file_size < 0) {
> +            error_setg_errno(errp, -old_file_size,
> +                             "Failed to inquire current file length");
> +            return ret;
> +        }
> +
> +        nb_new_data_clusters = DIV_ROUND_UP(offset - old_length,
> +                                            s->cluster_size);
> +
> +        /* This is an overestimation; we will not actually allocate space for
> +         * these in the file but just make sure the new refcount structures are
> +         * able to cover them so we will not have to allocate new refblocks
> +         * while entering the data blocks in the potentially new L2 tables.
> +         * (We do not actually care where the L2 tables are placed. Maybe they
> +         *  are already allocated or they can be placed somewhere before
> +         *  @old_file_size. It does not matter because they will be fully
> +         *  allocated automatically, so they do not need to be covered by the
> +         *  preallocation. All that matters is that we will not have to allocate
> +         *  new refcount structures for them.) */
> +        nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters,
> +                                        s->cluster_size / sizeof(uint64_t));
> +        /* The cluster range may not be aligned to L2 boundaries, so add one L2
> +         * table for a potential head/tail */
> +        nb_new_l2_tables++;
> +
> +        allocation_start = qcow2_refcount_area(bs, old_file_size,
> +                                               nb_new_data_clusters +
> +                                               nb_new_l2_tables,
> +                                               true, 0, 0);
> +        if (allocation_start < 0) {
> +            error_setg_errno(errp, -allocation_start,
> +                             "Failed to resize refcount structures");
> +            return -allocation_start;
> +        }
> +
> +        clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start,
> +                                                     nb_new_data_clusters);
> +        if (clusters_allocated < 0) {
> +            error_setg_errno(errp, -clusters_allocated,
> +                             "Failed to allocate data clusters");
> +            return -clusters_allocated;
> +        }
> +
> +        assert(clusters_allocated == nb_new_data_clusters);
> +
> +        /* Allocate the data area */
> +        new_file_size = allocation_start +
> +                        nb_new_data_clusters * s->cluster_size;
> +        ret = bdrv_truncate(bs->file, new_file_size, prealloc, errp);
> +        if (ret < 0) {
> +            error_prepend(errp, "Failed to resize underlying file: ");
> +            qcow2_free_clusters(bs, allocation_start,
> +                                nb_new_data_clusters * s->cluster_size,
> +                                QCOW2_DISCARD_OTHER);
> +            return ret;
> +        }
> +
> +        /* Create the necessary L2 entries */
> +        host_offset = allocation_start;
> +        guest_offset = old_length;
> +        while (nb_new_data_clusters) {
> +            int64_t guest_cluster = guest_offset >> s->cluster_bits;
> +            int64_t nb_clusters = MIN(nb_new_data_clusters,
> +                                      s->l2_size - guest_cluster % s->l2_size);
> +            QCowL2Meta allocation = {
> +                .offset       = guest_offset,
> +                .alloc_offset = host_offset,
> +                .nb_clusters  = nb_clusters,
> +            };
> +            qemu_co_queue_init(&allocation.dependent_requests);
> +
> +            ret = qcow2_alloc_cluster_link_l2(bs, &allocation);
> +            if (ret < 0) {
> +                error_setg_errno(errp, -ret, "Failed to update L2 tables");
> +                qcow2_free_clusters(bs, host_offset,
> +                                    nb_new_data_clusters * s->cluster_size,
> +                                    QCOW2_DISCARD_OTHER);
> +                return ret;
> +            }
> +
> +            guest_offset += nb_clusters * s->cluster_size;
> +            host_offset += nb_clusters * s->cluster_size;
> +            nb_new_data_clusters -= nb_clusters;
> +        }
> +        break;
> +    }

Flush L1/L2 tables?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

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

* Re: [Qemu-devel] [PATCH v3 04/16] qemu-img: Expose PreallocMode for resizing
  2017-05-30 20:57   ` Eric Blake
@ 2017-05-31 12:12     ` Max Reitz
  0 siblings, 0 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-31 12:12 UTC (permalink / raw)
  To: Eric Blake, qemu-block; +Cc: Kevin Wolf, qemu-devel, Stefan Hajnoczi

[-- Attachment #1: Type: text/plain, Size: 2336 bytes --]

On 2017-05-30 22:57, Eric Blake wrote:
> On 05/26/2017 11:55 AM, Max Reitz wrote:
>> Add a --preallocation command line option to qemu-img resize which can
>> be used to set the PreallocMode parameter of blk_truncate().
>>
>> Signed-off-by: Max Reitz <mreitz@redhat.com>
>> ---
>>  qemu-img.c    | 33 ++++++++++++++++++++++++++++++---
>>  qemu-img.texi |  7 ++++++-
>>  2 files changed, 36 insertions(+), 4 deletions(-)
> 
>> @@ -3553,8 +3566,16 @@ static int img_resize(int argc, char **argv)
>>          goto out;
>>      }
>>  
>> +    current_size = blk_getlength(blk);
>> +    if (current_size < 0) {
>> +        error_report("Failed to inquire current image length: %s",
>> +                     strerror(-current_size));
>> +        ret = -1;
>> +        goto out;
>> +    }
>> +
>>      if (relative) {
>> -        total_size = blk_getlength(blk) + n * relative;
>> +        total_size = current_size + n * relative;
> 
> You snuck in a bug fix here (reporting failure, rather than using a
> bogus total_size, if querying the size fails).  Please mention that in
> the commit message.
> 
>> @@ -541,6 +541,11 @@ After using this command to grow a disk image, you must use file system and
>>  partitioning tools inside the VM to actually begin using the new space on the
>>  device.
>>  
>> +When growing an image, the @code{--preallocation} option may be used to specify
>> +how the additional image area should be allocated on the host.  See the format
>> +description in the @code{NOTES} section which values are allowed.  Using this
>> +option may result in more data being allocated than necessary.
> 
> Should we tone it down a bit by saying 'slightly more data'?  (We'd
> rather over-estimate than fall short, but our over-estimation will
> probably be < 1% off, and not something drastic like an order of
> magnitude off).

I wanted to be on the safe side for future updates for other formats
(where I'm not sure how well we can do this).

I somehow have the feeling that XFS preallocation may play its game,
too, although I can't seem to trigger it...

Anyway, as long as we can justify, say, ~10 % as "slightly", I'm OK with
adding it.

> With the improved commit message,
> Reviewed-by: Eric Blake <eblake@redhat.com>

Will do, and as always: Thanks!

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 498 bytes --]

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

* Re: [Qemu-devel] [Qemu-block] [PATCH v3 14/16] block/qcow2: falloc/full preallocating growth
  2017-05-31 10:41   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
@ 2017-05-31 12:19     ` Max Reitz
  0 siblings, 0 replies; 28+ messages in thread
From: Max Reitz @ 2017-05-31 12:19 UTC (permalink / raw)
  To: Stefan Hajnoczi; +Cc: qemu-block, Kevin Wolf, qemu-devel, Stefan Hajnoczi

[-- Attachment #1: Type: text/plain, Size: 5334 bytes --]

On 2017-05-31 12:41, Stefan Hajnoczi wrote:
> On Fri, May 26, 2017 at 06:55:16PM +0200, Max Reitz wrote:
>> @@ -2677,6 +2679,102 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t offset,
>>          }
>>          break;
>>  
>> +    case PREALLOC_MODE_FALLOC:
>> +    case PREALLOC_MODE_FULL:
>> +    {
>> +        int64_t allocation_start, host_offset, guest_offset;
>> +        int64_t clusters_allocated;
>> +        int64_t old_file_size, new_file_size;
>> +        uint64_t nb_new_data_clusters, nb_new_l2_tables;
>> +
>> +        old_file_size = bdrv_getlength(bs->file->bs);
>> +        if (old_file_size < 0) {
>> +            error_setg_errno(errp, -old_file_size,
>> +                             "Failed to inquire current file length");
>> +            return ret;
>> +        }
>> +
>> +        nb_new_data_clusters = DIV_ROUND_UP(offset - old_length,
>> +                                            s->cluster_size);
>> +
>> +        /* This is an overestimation; we will not actually allocate space for
>> +         * these in the file but just make sure the new refcount structures are
>> +         * able to cover them so we will not have to allocate new refblocks
>> +         * while entering the data blocks in the potentially new L2 tables.
>> +         * (We do not actually care where the L2 tables are placed. Maybe they
>> +         *  are already allocated or they can be placed somewhere before
>> +         *  @old_file_size. It does not matter because they will be fully
>> +         *  allocated automatically, so they do not need to be covered by the
>> +         *  preallocation. All that matters is that we will not have to allocate
>> +         *  new refcount structures for them.) */
>> +        nb_new_l2_tables = DIV_ROUND_UP(nb_new_data_clusters,
>> +                                        s->cluster_size / sizeof(uint64_t));
>> +        /* The cluster range may not be aligned to L2 boundaries, so add one L2
>> +         * table for a potential head/tail */
>> +        nb_new_l2_tables++;
>> +
>> +        allocation_start = qcow2_refcount_area(bs, old_file_size,
>> +                                               nb_new_data_clusters +
>> +                                               nb_new_l2_tables,
>> +                                               true, 0, 0);
>> +        if (allocation_start < 0) {
>> +            error_setg_errno(errp, -allocation_start,
>> +                             "Failed to resize refcount structures");
>> +            return -allocation_start;
>> +        }
>> +
>> +        clusters_allocated = qcow2_alloc_clusters_at(bs, allocation_start,
>> +                                                     nb_new_data_clusters);
>> +        if (clusters_allocated < 0) {
>> +            error_setg_errno(errp, -clusters_allocated,
>> +                             "Failed to allocate data clusters");
>> +            return -clusters_allocated;
>> +        }
>> +
>> +        assert(clusters_allocated == nb_new_data_clusters);
>> +
>> +        /* Allocate the data area */
>> +        new_file_size = allocation_start +
>> +                        nb_new_data_clusters * s->cluster_size;
>> +        ret = bdrv_truncate(bs->file, new_file_size, prealloc, errp);
>> +        if (ret < 0) {
>> +            error_prepend(errp, "Failed to resize underlying file: ");
>> +            qcow2_free_clusters(bs, allocation_start,
>> +                                nb_new_data_clusters * s->cluster_size,
>> +                                QCOW2_DISCARD_OTHER);
>> +            return ret;
>> +        }
>> +
>> +        /* Create the necessary L2 entries */
>> +        host_offset = allocation_start;
>> +        guest_offset = old_length;
>> +        while (nb_new_data_clusters) {
>> +            int64_t guest_cluster = guest_offset >> s->cluster_bits;
>> +            int64_t nb_clusters = MIN(nb_new_data_clusters,
>> +                                      s->l2_size - guest_cluster % s->l2_size);
>> +            QCowL2Meta allocation = {
>> +                .offset       = guest_offset,
>> +                .alloc_offset = host_offset,
>> +                .nb_clusters  = nb_clusters,
>> +            };
>> +            qemu_co_queue_init(&allocation.dependent_requests);
>> +
>> +            ret = qcow2_alloc_cluster_link_l2(bs, &allocation);
>> +            if (ret < 0) {
>> +                error_setg_errno(errp, -ret, "Failed to update L2 tables");
>> +                qcow2_free_clusters(bs, host_offset,
>> +                                    nb_new_data_clusters * s->cluster_size,
>> +                                    QCOW2_DISCARD_OTHER);
>> +                return ret;
>> +            }
>> +
>> +            guest_offset += nb_clusters * s->cluster_size;
>> +            host_offset += nb_clusters * s->cluster_size;
>> +            nb_new_data_clusters -= nb_clusters;
>> +        }
>> +        break;
>> +    }
> 
> Flush L1/L2 tables?

Right. It would be good to actually have the area allocated for sure
before writing the new size. :-)

The same applies to the metadata-only preallocate(), though. So this
should probably be added in patch 11.

Thanks for spotting this, and thanks for reviewing!

Max


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 498 bytes --]

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

end of thread, other threads:[~2017-05-31 12:19 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-26 16:55 [Qemu-devel] [PATCH v3 00/16] block: Preallocated truncate Max Reitz
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 01/16] block: Add PreallocMode to BD.bdrv_truncate() Max Reitz
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 02/16] block: Add PreallocMode to bdrv_truncate() Max Reitz
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 03/16] block: Add PreallocMode to blk_truncate() Max Reitz
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 04/16] qemu-img: Expose PreallocMode for resizing Max Reitz
2017-05-30 20:57   ` Eric Blake
2017-05-31 12:12     ` Max Reitz
2017-05-31 10:09   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 05/16] block/file-posix: Small fixes in raw_create() Max Reitz
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 06/16] block/file-posix: Extract raw_regular_truncate() Max Reitz
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 07/16] block/file-posix: Generalize raw_regular_truncate Max Reitz
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 08/16] block/file-posix: Preallocation for truncate Max Reitz
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 09/16] block/qcow2: Generalize preallocate() Max Reitz
2017-05-30 21:26   ` Eric Blake
2017-05-31 10:22   ` Stefan Hajnoczi
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 10/16] block/qcow2: Lock s->lock in preallocate() Max Reitz
2017-05-30 21:28   ` Eric Blake
2017-05-31 10:24   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 11/16] block/qcow2: Metadata preallocation for truncate Max Reitz
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 12/16] block/qcow2: Add qcow2_refcount_area() Max Reitz
2017-05-31 10:36   ` Stefan Hajnoczi
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 13/16] block/qcow2: Rename "fail_block" to just "fail" Max Reitz
2017-05-31 10:36   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 14/16] block/qcow2: falloc/full preallocating growth Max Reitz
2017-05-31 10:41   ` [Qemu-devel] [Qemu-block] " Stefan Hajnoczi
2017-05-31 12:19     ` Max Reitz
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 15/16] iotests: Add preallocated resize test for raw Max Reitz
2017-05-26 16:55 ` [Qemu-devel] [PATCH v3 16/16] iotests: Add preallocated growth test for qcow2 Max Reitz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.