From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
To: qemu-block@nongnu.org
Cc: kwolf@redhat.com, vsementsov@virtuozzo.com,
qemu-devel@nongnu.org, mreitz@redhat.com, den@openvz.org,
jsnow@redhat.com
Subject: [PATCH v3 08/10] nbd/server: introduce NBDExtentArray
Date: Thu, 19 Dec 2019 13:03:46 +0300 [thread overview]
Message-ID: <20191219100348.24827-9-vsementsov@virtuozzo.com> (raw)
In-Reply-To: <20191219100348.24827-1-vsementsov@virtuozzo.com>
Introduce NBDExtentArray class, to handle extents list creation in more
controlled way and with less OUT parameters in functions.
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
---
nbd/server.c | 201 ++++++++++++++++++++++++++++-----------------------
1 file changed, 109 insertions(+), 92 deletions(-)
diff --git a/nbd/server.c b/nbd/server.c
index a4b348eb32..cc722adc31 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -1909,27 +1909,89 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
return ret;
}
+typedef struct NBDExtentArray {
+ NBDExtent *extents;
+ unsigned int nb_alloc;
+ unsigned int count;
+ uint64_t total_length;
+ bool converted; /* extents are converted to BE, no more changes allowed */
+} NBDExtentArray;
+
+static NBDExtentArray *nbd_extent_array_new(unsigned int nb_alloc)
+{
+ NBDExtentArray *ea = g_new0(NBDExtentArray, 1);
+
+ ea->nb_alloc = nb_alloc;
+ ea->extents = g_new(NBDExtent, nb_alloc);
+
+ return ea;
+}
+
+static void nbd_extent_array_free(NBDExtentArray *ea)
+{
+ g_free(ea->extents);
+ g_free(ea);
+}
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(NBDExtentArray, nbd_extent_array_free);
+
+/* Further modifications of the array after conversion are abandoned */
+static void nbd_extent_array_convert_to_be(NBDExtentArray *ea)
+{
+ int i;
+
+ if (ea->converted) {
+ return;
+ }
+ ea->converted = true;
+
+ for (i = 0; i < ea->count; i++) {
+ ea->extents[i].flags = cpu_to_be32(ea->extents[i].flags);
+ ea->extents[i].length = cpu_to_be32(ea->extents[i].length);
+ }
+}
+
/*
- * Populate @extents from block status. Update @bytes to be the actual
- * length encoded (which may be smaller than the original), and update
- * @nb_extents to the number of extents used.
- *
- * Returns zero on success and -errno on bdrv_block_status_above failure.
+ * Add extent to NBDExtentArray. If extent can't be added (no available space),
+ * return -1.
+ * For safety, when returning -1 for the first time, the array is converted
+ * to BE and further modifications are abandoned.
*/
-static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
- uint64_t *bytes, NBDExtent *extents,
- unsigned int *nb_extents)
+static int nbd_extent_array_add(NBDExtentArray *ea,
+ uint32_t length, uint32_t flags)
{
- uint64_t remaining_bytes = *bytes;
- NBDExtent *extent = extents, *extents_end = extents + *nb_extents;
- bool first_extent = true;
+ assert(!ea->converted);
+
+ if (!length) {
+ return 0;
+ }
+
+ /* Extend previous extent if flags are the same */
+ if (ea->count > 0 && flags == ea->extents[ea->count - 1].flags) {
+ ea->extents[ea->count - 1].length += length;
+ ea->total_length += length;
+ return 0;
+ }
+
+ if (ea->count >= ea->nb_alloc) {
+ nbd_extent_array_convert_to_be(ea);
+ return -1;
+ }
+
+ ea->total_length += length;
+ ea->extents[ea->count] = (NBDExtent) {.length = length, .flags = flags};
+ ea->count++;
- assert(*nb_extents);
- while (remaining_bytes) {
+ return 0;
+}
+
+static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
+ uint64_t bytes, NBDExtentArray *ea)
+{
+ while (bytes) {
uint32_t flags;
int64_t num;
- int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes,
- &num, NULL, NULL);
+ int ret = bdrv_block_status_above(bs, NULL, offset, bytes, &num,
+ NULL, NULL);
if (ret < 0) {
return ret;
@@ -1938,60 +2000,37 @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) |
(ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0);
- if (first_extent) {
- extent->flags = flags;
- extent->length = num;
- first_extent = false;
- } else if (flags == extent->flags) {
- /* extend current extent */
- extent->length += num;
- } else {
- if (extent + 1 == extents_end) {
- break;
- }
-
- /* start new extent */
- extent++;
- extent->flags = flags;
- extent->length = num;
+ if (nbd_extent_array_add(ea, num, flags) < 0) {
+ return 0;
}
- offset += num;
- remaining_bytes -= num;
- }
-
- extents_end = extent + 1;
- for (extent = extents; extent < extents_end; extent++) {
- extent->flags = cpu_to_be32(extent->flags);
- extent->length = cpu_to_be32(extent->length);
+ offset += num;
+ bytes -= num;
}
- *bytes -= remaining_bytes;
- *nb_extents = extents_end - extents;
-
return 0;
}
-/* nbd_co_send_extents
+/*
+ * nbd_co_send_extents
*
- * @length is only for tracing purposes (and may be smaller or larger
- * than the client's original request). @last controls whether
- * NBD_REPLY_FLAG_DONE is sent. @extents should already be in
- * big-endian format.
+ * @ea is converted to BE by the function
+ * @last controls whether NBD_REPLY_FLAG_DONE is sent.
*/
static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
- NBDExtent *extents, unsigned int nb_extents,
- uint64_t length, bool last,
- uint32_t context_id, Error **errp)
+ NBDExtentArray *ea,
+ bool last, uint32_t context_id, Error **errp)
{
NBDStructuredMeta chunk;
-
struct iovec iov[] = {
{.iov_base = &chunk, .iov_len = sizeof(chunk)},
- {.iov_base = extents, .iov_len = nb_extents * sizeof(extents[0])}
+ {.iov_base = ea->extents, .iov_len = ea->count * sizeof(ea->extents[0])}
};
- trace_nbd_co_send_extents(handle, nb_extents, context_id, length, last);
+ nbd_extent_array_convert_to_be(ea);
+
+ trace_nbd_co_send_extents(handle, ea->count, context_id, ea->total_length,
+ last);
set_be_chunk(&chunk.h, last ? NBD_REPLY_FLAG_DONE : 0,
NBD_REPLY_TYPE_BLOCK_STATUS,
handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
@@ -2009,39 +2048,27 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle,
{
int ret;
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
- NBDExtent *extents = g_new(NBDExtent, nb_extents);
- uint64_t final_length = length;
+ g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents);
- ret = blockstatus_to_extents(bs, offset, &final_length, extents,
- &nb_extents);
+ ret = blockstatus_to_extents(bs, offset, length, ea);
if (ret < 0) {
- g_free(extents);
return nbd_co_send_structured_error(
client, handle, -ret, "can't get block status", errp);
}
- ret = nbd_co_send_extents(client, handle, extents, nb_extents,
- final_length, last, context_id, errp);
-
- g_free(extents);
-
- return ret;
+ return nbd_co_send_extents(client, handle, ea, last, context_id, errp);
}
/*
- * Populate @extents from a dirty bitmap. Unless @dont_fragment, the
- * final extent may exceed the original @length. Store in @length the
- * byte length encoded (which may be smaller or larger than the
- * original), and return the number of extents used.
+ * Populate @ea from a dirty bitmap. Unless @dont_fragment, the
+ * final extent may exceed the original @length.
*/
-static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
- uint64_t *length, NBDExtent *extents,
- unsigned int nb_extents,
- bool dont_fragment)
+static void bitmap_to_extents(BdrvDirtyBitmap *bitmap,
+ uint64_t offset, uint64_t length,
+ NBDExtentArray *ea, bool dont_fragment)
{
uint64_t begin = offset, end = offset;
- uint64_t overall_end = offset + *length;
- unsigned int i = 0;
+ uint64_t overall_end = offset + length;
BdrvDirtyBitmapIter *it;
bool dirty;
@@ -2050,8 +2077,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
it = bdrv_dirty_iter_new(bitmap);
dirty = bdrv_dirty_bitmap_get_locked(bitmap, offset);
- assert(begin < overall_end && nb_extents);
- while (begin < overall_end && i < nb_extents) {
+ while (begin < overall_end) {
bool next_dirty = !dirty;
if (dirty) {
@@ -2071,9 +2097,10 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
end = overall_end;
}
- extents[i].length = cpu_to_be32(end - begin);
- extents[i].flags = cpu_to_be32(dirty ? NBD_STATE_DIRTY : 0);
- i++;
+ if (nbd_extent_array_add(ea, end - begin,
+ dirty ? NBD_STATE_DIRTY : 0) < 0) {
+ break;
+ }
begin = end;
dirty = next_dirty;
}
@@ -2083,8 +2110,6 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
bdrv_dirty_bitmap_unlock(bitmap);
assert(offset < end);
- *length = end - offset;
- return i;
}
static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle,
@@ -2092,20 +2117,12 @@ static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle,
uint32_t length, bool dont_fragment, bool last,
uint32_t context_id, Error **errp)
{
- int ret;
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
- NBDExtent *extents = g_new(NBDExtent, nb_extents);
- uint64_t final_length = length;
+ g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents);
- nb_extents = bitmap_to_extents(bitmap, offset, &final_length, extents,
- nb_extents, dont_fragment);
+ bitmap_to_extents(bitmap, offset, length, ea, dont_fragment);
- ret = nbd_co_send_extents(client, handle, extents, nb_extents,
- final_length, last, context_id, errp);
-
- g_free(extents);
-
- return ret;
+ return nbd_co_send_extents(client, handle, ea, last, context_id, errp);
}
/* nbd_co_receive_request
--
2.21.0
next prev parent reply other threads:[~2019-12-19 10:09 UTC|newest]
Thread overview: 39+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-12-19 10:03 [PATCH v3 00/10] Further bitmaps improvements Vladimir Sementsov-Ogievskiy
2019-12-19 10:03 ` [PATCH v3 01/10] hbitmap: assert that we don't create bitmap larger than INT64_MAX Vladimir Sementsov-Ogievskiy
2020-01-20 10:51 ` Max Reitz
2019-12-19 10:03 ` [PATCH v3 02/10] hbitmap: move hbitmap_iter_next_word to hbitmap.c Vladimir Sementsov-Ogievskiy
2020-01-20 10:55 ` Max Reitz
2020-01-20 16:14 ` Vladimir Sementsov-Ogievskiy
2019-12-19 10:03 ` [PATCH v3 03/10] hbitmap: unpublish hbitmap_iter_skip_words Vladimir Sementsov-Ogievskiy
2020-01-20 10:59 ` Max Reitz
2019-12-19 10:03 ` [PATCH v3 04/10] hbitmap: drop meta bitmaps as they are unused Vladimir Sementsov-Ogievskiy
2020-01-20 11:13 ` Max Reitz
2020-01-20 16:20 ` Vladimir Sementsov-Ogievskiy
2020-01-20 17:05 ` Max Reitz
2020-01-20 17:28 ` Vladimir Sementsov-Ogievskiy
2020-01-20 19:53 ` Eric Blake
2020-01-21 9:15 ` Vladimir Sementsov-Ogievskiy
2019-12-19 10:03 ` [PATCH v3 05/10] block/dirty-bitmap: switch _next_dirty_area and _next_zero to int64_t Vladimir Sementsov-Ogievskiy
2020-01-20 11:59 ` Max Reitz
2020-01-20 12:28 ` Vladimir Sementsov-Ogievskiy
2020-01-20 12:53 ` Max Reitz
2020-01-20 19:56 ` Eric Blake
2019-12-19 10:03 ` [PATCH v3 06/10] block/dirty-bitmap: add _next_dirty API Vladimir Sementsov-Ogievskiy
2020-01-20 13:14 ` Max Reitz
2020-01-20 16:30 ` Vladimir Sementsov-Ogievskiy
2020-01-21 9:35 ` Vladimir Sementsov-Ogievskiy
2019-12-19 10:03 ` [PATCH v3 07/10] block/dirty-bitmap: improve _next_dirty_area API Vladimir Sementsov-Ogievskiy
2020-01-20 13:58 ` Max Reitz
2020-01-20 16:26 ` Vladimir Sementsov-Ogievskiy
2019-12-19 10:03 ` Vladimir Sementsov-Ogievskiy [this message]
2020-01-20 20:20 ` [PATCH v3 08/10] nbd/server: introduce NBDExtentArray Eric Blake
2020-01-21 10:25 ` Vladimir Sementsov-Ogievskiy
2019-12-19 10:03 ` [PATCH v3 09/10] nbd/server: use bdrv_dirty_bitmap_next_dirty_area Vladimir Sementsov-Ogievskiy
2020-01-20 20:23 ` Eric Blake
2019-12-19 10:03 ` [PATCH v3 10/10] block/qcow2-bitmap: use bdrv_dirty_bitmap_next_dirty Vladimir Sementsov-Ogievskiy
2020-01-20 14:18 ` Max Reitz
2020-01-20 16:05 ` Vladimir Sementsov-Ogievskiy
2020-01-20 9:08 ` [PATCH v3 00/10] Further bitmaps improvements Vladimir Sementsov-Ogievskiy
2020-01-20 14:20 ` Max Reitz
2020-01-20 16:33 ` Vladimir Sementsov-Ogievskiy
2020-01-20 20:25 ` Eric Blake
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=20191219100348.24827-9-vsementsov@virtuozzo.com \
--to=vsementsov@virtuozzo.com \
--cc=den@openvz.org \
--cc=jsnow@redhat.com \
--cc=kwolf@redhat.com \
--cc=mreitz@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
/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 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.