qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Max Reitz <mreitz@redhat.com>
To: qemu-block@nongnu.org
Cc: qemu-devel@nongnu.org, Max Reitz <mreitz@redhat.com>,
	Kevin Wolf <kwolf@redhat.com>,
	Stefan Hajnoczi <stefanha@redhat.com>
Subject: [Qemu-devel] [PATCH v3 14/16] block/qcow2: falloc/full preallocating growth
Date: Fri, 26 May 2017 18:55:16 +0200	[thread overview]
Message-ID: <20170526165518.7580-15-mreitz@redhat.com> (raw)
In-Reply-To: <20170526165518.7580-1-mreitz@redhat.com>

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

  parent reply	other threads:[~2017-05-26 16:56 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 ` Max Reitz [this message]
2017-05-31 10:41   ` [Qemu-devel] [Qemu-block] [PATCH v3 14/16] block/qcow2: falloc/full preallocating growth 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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170526165518.7580-15-mreitz@redhat.com \
    --to=mreitz@redhat.com \
    --cc=kwolf@redhat.com \
    --cc=qemu-block@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).