nbd/server: introduce NBDExtentArray
Introduce NBDExtentArray class, to handle extents list creation in more controlled way and with fewer OUT parameters in functions. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Reviewed-by: Eric Blake <eblake@redhat.com> Message-id: 20200205112041.6003-9-vsementsov@virtuozzo.com Signed-off-by: John Snow <jsnow@redhat.com>
This commit is contained in:
parent
299ea9ff01
commit
89cbc7e308
216
nbd/server.c
216
nbd/server.c
@ -1909,27 +1909,98 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
typedef struct NBDExtentArray {
|
||||||
* Populate @extents from block status. Update @bytes to be the actual
|
NBDExtent *extents;
|
||||||
* length encoded (which may be smaller than the original), and update
|
unsigned int nb_alloc;
|
||||||
* @nb_extents to the number of extents used.
|
unsigned int count;
|
||||||
*
|
uint64_t total_length;
|
||||||
* Returns zero on success and -errno on bdrv_block_status_above failure.
|
bool can_add;
|
||||||
*/
|
bool converted_to_be;
|
||||||
static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
|
} NBDExtentArray;
|
||||||
uint64_t *bytes, NBDExtent *extents,
|
|
||||||
unsigned int *nb_extents)
|
|
||||||
{
|
|
||||||
uint64_t remaining_bytes = *bytes;
|
|
||||||
NBDExtent *extent = extents, *extents_end = extents + *nb_extents;
|
|
||||||
bool first_extent = true;
|
|
||||||
|
|
||||||
assert(*nb_extents);
|
static NBDExtentArray *nbd_extent_array_new(unsigned int nb_alloc)
|
||||||
while (remaining_bytes) {
|
{
|
||||||
|
NBDExtentArray *ea = g_new0(NBDExtentArray, 1);
|
||||||
|
|
||||||
|
ea->nb_alloc = nb_alloc;
|
||||||
|
ea->extents = g_new(NBDExtent, nb_alloc);
|
||||||
|
ea->can_add = true;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
assert(!ea->converted_to_be);
|
||||||
|
ea->can_add = false;
|
||||||
|
ea->converted_to_be = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add extent to NBDExtentArray. If extent can't be added (no available space),
|
||||||
|
* return -1.
|
||||||
|
* For safety, when returning -1 for the first time, .can_add is set to false,
|
||||||
|
* further call to nbd_extent_array_add() will crash.
|
||||||
|
* (to avoid the situation, when after failing to add an extent (returned -1),
|
||||||
|
* user miss this failure and add another extent, which is successfully added
|
||||||
|
* (array is full, but new extent may be squashed into the last one), then we
|
||||||
|
* have invalid array with skipped extent)
|
||||||
|
*/
|
||||||
|
static int nbd_extent_array_add(NBDExtentArray *ea,
|
||||||
|
uint32_t length, uint32_t flags)
|
||||||
|
{
|
||||||
|
assert(ea->can_add);
|
||||||
|
|
||||||
|
if (!length) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extend previous extent if flags are the same */
|
||||||
|
if (ea->count > 0 && flags == ea->extents[ea->count - 1].flags) {
|
||||||
|
uint64_t sum = (uint64_t)length + ea->extents[ea->count - 1].length;
|
||||||
|
|
||||||
|
if (sum <= UINT32_MAX) {
|
||||||
|
ea->extents[ea->count - 1].length = sum;
|
||||||
|
ea->total_length += length;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ea->count >= ea->nb_alloc) {
|
||||||
|
ea->can_add = false;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ea->total_length += length;
|
||||||
|
ea->extents[ea->count] = (NBDExtent) {.length = length, .flags = flags};
|
||||||
|
ea->count++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
|
||||||
|
uint64_t bytes, NBDExtentArray *ea)
|
||||||
|
{
|
||||||
|
while (bytes) {
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
int64_t num;
|
int64_t num;
|
||||||
int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes,
|
int ret = bdrv_block_status_above(bs, NULL, offset, bytes, &num,
|
||||||
&num, NULL, NULL);
|
NULL, NULL);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
return ret;
|
return ret;
|
||||||
@ -1938,60 +2009,37 @@ static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset,
|
|||||||
flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) |
|
flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) |
|
||||||
(ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0);
|
(ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0);
|
||||||
|
|
||||||
if (first_extent) {
|
if (nbd_extent_array_add(ea, num, flags) < 0) {
|
||||||
extent->flags = flags;
|
return 0;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
offset += num;
|
offset += num;
|
||||||
remaining_bytes -= num;
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
*bytes -= remaining_bytes;
|
|
||||||
*nb_extents = extents_end - extents;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* nbd_co_send_extents
|
/*
|
||||||
|
* nbd_co_send_extents
|
||||||
*
|
*
|
||||||
* @length is only for tracing purposes (and may be smaller or larger
|
* @ea is converted to BE by the function
|
||||||
* than the client's original request). @last controls whether
|
* @last controls whether NBD_REPLY_FLAG_DONE is sent.
|
||||||
* NBD_REPLY_FLAG_DONE is sent. @extents should already be in
|
|
||||||
* big-endian format.
|
|
||||||
*/
|
*/
|
||||||
static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
|
static int nbd_co_send_extents(NBDClient *client, uint64_t handle,
|
||||||
NBDExtent *extents, unsigned int nb_extents,
|
NBDExtentArray *ea,
|
||||||
uint64_t length, bool last,
|
bool last, uint32_t context_id, Error **errp)
|
||||||
uint32_t context_id, Error **errp)
|
|
||||||
{
|
{
|
||||||
NBDStructuredMeta chunk;
|
NBDStructuredMeta chunk;
|
||||||
|
|
||||||
struct iovec iov[] = {
|
struct iovec iov[] = {
|
||||||
{.iov_base = &chunk, .iov_len = sizeof(chunk)},
|
{.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,
|
set_be_chunk(&chunk.h, last ? NBD_REPLY_FLAG_DONE : 0,
|
||||||
NBD_REPLY_TYPE_BLOCK_STATUS,
|
NBD_REPLY_TYPE_BLOCK_STATUS,
|
||||||
handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
|
handle, sizeof(chunk) - sizeof(chunk.h) + iov[1].iov_len);
|
||||||
@ -2009,39 +2057,27 @@ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle,
|
|||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
|
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
|
||||||
NBDExtent *extents = g_new(NBDExtent, nb_extents);
|
g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents);
|
||||||
uint64_t final_length = length;
|
|
||||||
|
|
||||||
ret = blockstatus_to_extents(bs, offset, &final_length, extents,
|
ret = blockstatus_to_extents(bs, offset, length, ea);
|
||||||
&nb_extents);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
g_free(extents);
|
|
||||||
return nbd_co_send_structured_error(
|
return nbd_co_send_structured_error(
|
||||||
client, handle, -ret, "can't get block status", errp);
|
client, handle, -ret, "can't get block status", errp);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = nbd_co_send_extents(client, handle, extents, nb_extents,
|
return nbd_co_send_extents(client, handle, ea, last, context_id, errp);
|
||||||
final_length, last, context_id, errp);
|
|
||||||
|
|
||||||
g_free(extents);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Populate @extents from a dirty bitmap. Unless @dont_fragment, the
|
* Populate @ea from a dirty bitmap. Unless @dont_fragment, the
|
||||||
* final extent may exceed the original @length. Store in @length the
|
* final extent may exceed the original @length.
|
||||||
* byte length encoded (which may be smaller or larger than the
|
|
||||||
* original), and return the number of extents used.
|
|
||||||
*/
|
*/
|
||||||
static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
static void bitmap_to_extents(BdrvDirtyBitmap *bitmap,
|
||||||
uint64_t *length, NBDExtent *extents,
|
uint64_t offset, uint64_t length,
|
||||||
unsigned int nb_extents,
|
NBDExtentArray *ea, bool dont_fragment)
|
||||||
bool dont_fragment)
|
|
||||||
{
|
{
|
||||||
uint64_t begin = offset, end = offset;
|
uint64_t begin = offset, end = offset;
|
||||||
uint64_t overall_end = offset + *length;
|
uint64_t overall_end = offset + length;
|
||||||
unsigned int i = 0;
|
|
||||||
BdrvDirtyBitmapIter *it;
|
BdrvDirtyBitmapIter *it;
|
||||||
bool dirty;
|
bool dirty;
|
||||||
|
|
||||||
@ -2050,8 +2086,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
|||||||
it = bdrv_dirty_iter_new(bitmap);
|
it = bdrv_dirty_iter_new(bitmap);
|
||||||
dirty = bdrv_dirty_bitmap_get_locked(bitmap, offset);
|
dirty = bdrv_dirty_bitmap_get_locked(bitmap, offset);
|
||||||
|
|
||||||
assert(begin < overall_end && nb_extents);
|
while (begin < overall_end) {
|
||||||
while (begin < overall_end && i < nb_extents) {
|
|
||||||
bool next_dirty = !dirty;
|
bool next_dirty = !dirty;
|
||||||
|
|
||||||
if (dirty) {
|
if (dirty) {
|
||||||
@ -2071,9 +2106,10 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
|||||||
end = overall_end;
|
end = overall_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
extents[i].length = cpu_to_be32(end - begin);
|
if (nbd_extent_array_add(ea, end - begin,
|
||||||
extents[i].flags = cpu_to_be32(dirty ? NBD_STATE_DIRTY : 0);
|
dirty ? NBD_STATE_DIRTY : 0) < 0) {
|
||||||
i++;
|
break;
|
||||||
|
}
|
||||||
begin = end;
|
begin = end;
|
||||||
dirty = next_dirty;
|
dirty = next_dirty;
|
||||||
}
|
}
|
||||||
@ -2083,8 +2119,6 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset,
|
|||||||
bdrv_dirty_bitmap_unlock(bitmap);
|
bdrv_dirty_bitmap_unlock(bitmap);
|
||||||
|
|
||||||
assert(offset < end);
|
assert(offset < end);
|
||||||
*length = end - offset;
|
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle,
|
static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle,
|
||||||
@ -2092,20 +2126,12 @@ static int nbd_co_send_bitmap(NBDClient *client, uint64_t handle,
|
|||||||
uint32_t length, bool dont_fragment, bool last,
|
uint32_t length, bool dont_fragment, bool last,
|
||||||
uint32_t context_id, Error **errp)
|
uint32_t context_id, Error **errp)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
|
unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BLOCK_STATUS_EXTENTS;
|
||||||
NBDExtent *extents = g_new(NBDExtent, nb_extents);
|
g_autoptr(NBDExtentArray) ea = nbd_extent_array_new(nb_extents);
|
||||||
uint64_t final_length = length;
|
|
||||||
|
|
||||||
nb_extents = bitmap_to_extents(bitmap, offset, &final_length, extents,
|
bitmap_to_extents(bitmap, offset, length, ea, dont_fragment);
|
||||||
nb_extents, dont_fragment);
|
|
||||||
|
|
||||||
ret = nbd_co_send_extents(client, handle, extents, nb_extents,
|
return nbd_co_send_extents(client, handle, ea, last, context_id, errp);
|
||||||
final_length, last, context_id, errp);
|
|
||||||
|
|
||||||
g_free(extents);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* nbd_co_receive_request
|
/* nbd_co_receive_request
|
||||||
|
Loading…
Reference in New Issue
Block a user