From 016851500321cc67c4c650d4091949f4a58a3201 Mon Sep 17 00:00:00 2001 From: Derek Su Date: Thu, 7 Sep 2023 13:38:24 +0800 Subject: [PATCH] fragmap: implement bdev_lvol_get_fragmap Get a fragmap for a specific segment of a logical volume using the provided offset and size. A fragmap is a bitmap that records the allocation status of clusters. A value of "1" indicates that a cluster is allocated, whereas "0" signifies that a cluster is unallocated. Longhorn 6138 Signed-off-by: Derek Su --- doc/jsonrpc.md | 47 ++++++++ include/spdk/bit_array.h | 8 ++ include/spdk/lvol.h | 11 ++ include/spdk/util.h | 14 +++ include/spdk_internal/lvolstore.h | 24 ++++ lib/util/bit_array.c | 47 ++++++++ module/bdev/lvol/vbdev_lvol.c | 183 ++++++++++++++++++++++++++++++ module/bdev/lvol/vbdev_lvol.h | 13 +++ module/bdev/lvol/vbdev_lvol_rpc.c | 95 ++++++++++++++++ python/spdk/rpc/lvol.py | 19 ++++ scripts/rpc.py | 11 ++ test/lvol/fragmap.sh | 52 +++++++++ 12 files changed, 524 insertions(+) create mode 100755 test/lvol/fragmap.sh diff --git a/doc/jsonrpc.md b/doc/jsonrpc.md index d797dca60..5f67f7e61 100644 --- a/doc/jsonrpc.md +++ b/doc/jsonrpc.md @@ -496,6 +496,7 @@ Example response: "bdev_lvol_rename_lvstore", "bdev_lvol_create_lvstore", "bdev_lvol_shallow_copy", + "bdev_lvol_get_fragmap", "bdev_daos_delete", "bdev_daos_create", "bdev_daos_resize" @@ -10084,6 +10085,52 @@ Example response: } ~~~ +### bdev_lvol_get_fragmap {#bdev_lvol_get_fragmap} + +Get a fragmap for a specific segment of a logical volume using the provided offset and size. +A fragmap is a bitmap that records the allocation status of clusters. A value of "1" indicates +that a cluster is allocated, whereas "0" signifies that a cluster is unallocated. + +#### Parameters + +Name | Optional | Type | Description +----------------------- | -------- | ----------- | ----------- +name | Required | string | UUID or alias of the logical volume +offset | Optional | number | Offset in bytes of the specific segment of the logical volume (Default: 0) +size | Optional | number | Size in bytes of the specific segment of the logical volume (Default: 0 for representing the entire file) + +#### Example + +Example request: + +~~~json +{ + "jsonrpc": "2.0", + "method": "bdev_lvol_get_fragmap", + "id": 1, + "params": { + "name": "8a47421a-20cf-444f-845c-d97ad0b0bd8e", + "offset": 0, + "size": 41943040 + } +} +~~~ + +Example response: + +~~~json +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "cluster_size": 4194304, + "num_clusters": 10, + "num_allocated_clusters": 0, + "fragmap": "AAA=" + } +} +~~~ + ## RAID ### bdev_raid_get_bdevs {#rpc_bdev_raid_get_bdevs} diff --git a/include/spdk/bit_array.h b/include/spdk/bit_array.h index b0fed89b0..317119c63 100644 --- a/include/spdk/bit_array.h +++ b/include/spdk/bit_array.h @@ -168,6 +168,14 @@ void spdk_bit_array_load_mask(struct spdk_bit_array *ba, const void *mask); */ void spdk_bit_array_clear_mask(struct spdk_bit_array *ba); +/** + * Encode a bit array into a base64 string. + * + * @param array Bit array to encode. + * @return base64 string. + */ +char *spdk_bit_array_to_base64_string(const struct spdk_bit_array *array); + #ifdef __cplusplus } #endif diff --git a/include/spdk/lvol.h b/include/spdk/lvol.h index eb66a403d..175506031 100644 --- a/include/spdk/lvol.h +++ b/include/spdk/lvol.h @@ -22,6 +22,7 @@ extern "C" { struct spdk_bs_dev; struct spdk_lvol_store; struct spdk_lvol; +struct spdk_fragmap; enum lvol_clear_method { LVOL_CLEAR_WITH_DEFAULT = BLOB_CLEAR_WITH_DEFAULT, @@ -116,6 +117,16 @@ typedef void (*spdk_lvol_op_with_handle_complete)(void *cb_arg, struct spdk_lvol */ typedef void (*spdk_lvol_op_complete)(void *cb_arg, int lvolerrno); +/** + * Callback definition for lvol operations with handle to fragmap + * + * @param cb_arg Custom arguments + * @param fragmap Handle to fragmap or NULL when lvolerrno is set + * @param lvolerrno Error + */ +typedef void (*spdk_lvol_op_with_fragmap_handle_complete)(void *cb_arg, + struct spdk_fragmap *fragmap, int lvolerrno); + /** * Callback definition for spdk_lvol_iter_clones. * diff --git a/include/spdk/util.h b/include/spdk/util.h index 3cb053250..709363f6f 100644 --- a/include/spdk/util.h +++ b/include/spdk/util.h @@ -308,6 +308,20 @@ spdk_memset_s(void *data, size_t data_size, int ch, size_t count) #endif } +/** + * @brief Check if \b dividend is divisible by \b divisor + * + * @param dividend Dividend + * @param divisor Divisor which is a power of 2 + * @return true + * @return false + */ +static inline bool +spdk_is_divisible_by(uint64_t dividend, uint64_t divisor) +{ + return (dividend & (divisor - 1)) == 0; +} + #ifdef __cplusplus } #endif diff --git a/include/spdk_internal/lvolstore.h b/include/spdk_internal/lvolstore.h index b2cda9c5d..1de274e08 100644 --- a/include/spdk_internal/lvolstore.h +++ b/include/spdk_internal/lvolstore.h @@ -115,6 +115,30 @@ struct spdk_lvol { TAILQ_ENTRY(spdk_lvol) degraded_link; }; +struct spdk_fragmap { + struct spdk_bit_array *map; + + uint64_t cluster_size; + uint64_t block_size; + uint64_t num_clusters; + uint64_t num_allocated_clusters; +}; + +struct spdk_fragmap_req { + struct spdk_bdev *bdev; + struct spdk_bdev_desc *bdev_desc; + struct spdk_io_channel *bdev_io_channel; + + struct spdk_fragmap fragmap; + + uint64_t offset; + uint64_t size; + uint64_t current_offset; + + spdk_lvol_op_with_fragmap_handle_complete cb_fn; + void *cb_arg; +}; + struct lvol_store_bdev *vbdev_lvol_store_first(void); struct lvol_store_bdev *vbdev_lvol_store_next(struct lvol_store_bdev *prev); diff --git a/lib/util/bit_array.c b/lib/util/bit_array.c index ec182492f..25de4f0b7 100644 --- a/lib/util/bit_array.c +++ b/lib/util/bit_array.c @@ -11,6 +11,7 @@ #include "spdk/likely.h" #include "spdk/util.h" +#include "spdk/base64.h" typedef uint64_t spdk_bit_array_word; #define SPDK_BIT_ARRAY_WORD_TZCNT(x) (__builtin_ctzll(x)) @@ -491,3 +492,49 @@ spdk_bit_pool_free_all_bits(struct spdk_bit_pool *pool) pool->lowest_free_bit = 0; pool->free_count = spdk_bit_array_capacity(pool->array); } + +static int +bits_to_bytes(const int bits) +{ + return ((bits + 7) >> 3); +} + +char * +spdk_bit_array_to_base64_string(const struct spdk_bit_array *array) +{ + uint32_t bit_count = spdk_bit_array_capacity(array); + size_t byte_count = bits_to_bytes(bit_count); + void *bytes; + char *encoded; + size_t total_size; + int rc; + + bytes = calloc(byte_count, sizeof(char)); + if (bytes == NULL) { + return NULL; + } + + for (uint32_t i = 0; i < bit_count; i++) { + if (spdk_bit_array_get(array, i)) { + // Set the bit in bytes's correct position + ((uint8_t *)bytes)[i / 8] |= 1 << (i % 8); + } + } + + total_size = spdk_base64_get_encoded_strlen(byte_count) + 1; + encoded = calloc(total_size, sizeof(char)); + if (encoded == NULL) { + free(bytes); + return NULL; + } + + rc = spdk_base64_encode(encoded, bytes, byte_count); + if (rc != 0) { + free(bytes); + free(encoded); + return NULL; + } + + free(bytes); + return encoded; +} \ No newline at end of file diff --git a/module/bdev/lvol/vbdev_lvol.c b/module/bdev/lvol/vbdev_lvol.c index e29d0a05c..1df170ac2 100644 --- a/module/bdev/lvol/vbdev_lvol.c +++ b/module/bdev/lvol/vbdev_lvol.c @@ -11,6 +11,8 @@ #include "spdk/string.h" #include "spdk/uuid.h" #include "spdk/blob.h" +#include "spdk/bit_array.h" +#include "spdk/base64.h" #include "vbdev_lvol.h" @@ -2087,4 +2089,185 @@ vbdev_lvol_shallow_copy(struct spdk_lvol *lvol, const char *bdev_name, spdk_lvol_shallow_copy(lvol, ext_dev, _vbdev_lvol_shallow_copy_cb, req); } +static void seek_hole_done_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg); + +static void +get_fragmap_done(struct spdk_fragmap_req *req, int error_code, const char *error_msg) +{ + req->cb_fn(req->cb_arg, &req->fragmap, error_code); + + spdk_bit_array_free(&req->fragmap.map); + spdk_put_io_channel(req->bdev_io_channel); + spdk_bdev_close(req->bdev_desc); + free(req); +} + +static void +seek_data_done_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) +{ + struct spdk_fragmap_req *req = cb_arg; + uint64_t next_data_offset_blocks; + int rc; + + next_data_offset_blocks = spdk_bdev_io_get_seek_offset(bdev_io); + spdk_bdev_free_io(bdev_io); + + req->current_offset = next_data_offset_blocks * req->fragmap.block_size; + + if (next_data_offset_blocks == UINT64_MAX || req->current_offset >= req->offset + req->size) { + get_fragmap_done(req, 0, NULL); + return; + } + + rc = spdk_bdev_seek_hole(req->bdev_desc, req->bdev_io_channel, + spdk_divide_round_up(req->current_offset, req->fragmap.block_size), + seek_hole_done_cb, req); + if (rc != 0) { + get_fragmap_done(req, rc, "failed to seek hole"); + } +} + +static void +seek_hole_done_cb(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg) +{ + struct spdk_fragmap_req *req = cb_arg; + uint64_t next_offset; + uint64_t start_cluster; + uint64_t num_clusters; + int rc; + + next_offset = spdk_bdev_io_get_seek_offset(bdev_io) * req->fragmap.block_size; + spdk_bdev_free_io(bdev_io); + + next_offset = spdk_min(next_offset, req->offset + req->size); + + start_cluster = spdk_divide_round_up(req->current_offset - req->offset, req->fragmap.cluster_size); + num_clusters = spdk_divide_round_up(next_offset - req->current_offset, req->fragmap.cluster_size); + + for (uint64_t i = 0; i < num_clusters; i++) { + spdk_bit_array_set(req->fragmap.map, start_cluster + i); + } + req->fragmap.num_allocated_clusters += num_clusters; + + req->current_offset = next_offset; + + if (req->current_offset == req->offset + req->size) { + get_fragmap_done(req, 0, NULL); + return; + } + + rc = spdk_bdev_seek_data(req->bdev_desc, req->bdev_io_channel, + spdk_divide_round_up(req->current_offset, req->fragmap.block_size), + seek_data_done_cb, req); + if (rc != 0) { + get_fragmap_done(req, rc, "failed to seek data"); + } +} + +static void +dummy_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *ctx) +{ +} + +void +vbdev_lvol_get_fragmap(struct spdk_lvol *lvol, uint64_t offset, uint64_t size, + spdk_lvol_op_with_fragmap_handle_complete cb_fn, void *cb_arg) +{ + struct spdk_bdev_desc *desc; + struct spdk_io_channel *channel; + struct spdk_bit_array *fragmap; + struct spdk_fragmap_req *req; + uint64_t cluster_size, num_clusters, block_size, num_blocks, lvol_size, segment_size; + int rc; + + // Create a bitmap recording the allocated clusters + cluster_size = spdk_bs_get_cluster_size(lvol->lvol_store->blobstore); + block_size = spdk_bdev_get_block_size(lvol->bdev); + num_blocks = spdk_bdev_get_num_blocks(lvol->bdev); + lvol_size = num_blocks * block_size; + + if (offset + size > lvol_size) { + SPDK_ERRLOG("offset %lu and size %lu exceed lvol size %lu\n", + offset, size, lvol_size); + cb_fn(cb_arg, NULL, -EINVAL); + return; + } + + segment_size = size; + if (size == 0) { + segment_size = lvol_size; + } + + if (!spdk_is_divisible_by(offset, cluster_size) || + !spdk_is_divisible_by(segment_size, cluster_size)) { + SPDK_ERRLOG("offset %lu and size %lu must be a multiple of cluster size %lu\n", + offset, segment_size, cluster_size); + cb_fn(cb_arg, NULL, -EINVAL); + return; + } + + num_clusters = spdk_divide_round_up(segment_size, cluster_size); + fragmap = spdk_bit_array_create(num_clusters); + if (fragmap == NULL) { + SPDK_ERRLOG("failed to allocate fragmap with num_clusters %lu\n", num_clusters); + cb_fn(cb_arg, NULL, -ENOMEM); + return; + } + + // Construct a fragmap of the lvol + rc = spdk_bdev_open_ext(lvol->bdev->name, false, + dummy_bdev_event_cb, NULL, &desc); + if (rc != 0) { + spdk_bit_array_free(&fragmap); + SPDK_ERRLOG("could not open bdev %s\n", lvol->bdev->name); + cb_fn(cb_arg, NULL, rc); + return; + } + + channel = spdk_bdev_get_io_channel(desc); + if (channel == NULL) { + spdk_bit_array_free(&fragmap); + spdk_bdev_close(desc); + SPDK_ERRLOG("could not allocate I/O channel.\n"); + cb_fn(cb_arg, NULL, -ENOMEM); + return; + } + + req = calloc(1, sizeof(struct spdk_fragmap_req)); + if (req == NULL) { + SPDK_ERRLOG("could not allocate fragmap_io\n"); + spdk_put_io_channel(channel); + spdk_bdev_close(desc); + spdk_bit_array_free(&fragmap); + cb_fn(cb_arg, NULL, -ENOMEM); + return; + } + + req->bdev = lvol->bdev; + req->bdev_desc = desc; + req->bdev_io_channel = channel; + req->offset = offset; + req->size = segment_size; + req->current_offset = offset; + req->cb_fn = cb_fn; + req->cb_arg = cb_arg; + req->fragmap.map = fragmap; + req->fragmap.num_clusters = num_clusters; + req->fragmap.block_size = block_size; + req->fragmap.cluster_size = cluster_size; + req->fragmap.num_allocated_clusters = 0; + + rc = spdk_bdev_seek_data(desc, channel, + spdk_divide_round_up(offset, block_size), + seek_data_done_cb, req); + if (rc != 0) { + SPDK_ERRLOG("failed to seek data\n"); + spdk_put_io_channel(channel); + spdk_bdev_close(desc); + spdk_bit_array_free(&fragmap); + free(req); + cb_fn(cb_arg, NULL, rc); + } +} + SPDK_LOG_REGISTER_COMPONENT(vbdev_lvol) diff --git a/module/bdev/lvol/vbdev_lvol.h b/module/bdev/lvol/vbdev_lvol.h index a26639276..1f5e7cb13 100644 --- a/module/bdev/lvol/vbdev_lvol.h +++ b/module/bdev/lvol/vbdev_lvol.h @@ -136,4 +136,17 @@ int vbdev_lvol_esnap_dev_create(void *bs_ctx, void *blob_ctx, struct spdk_blob * void vbdev_lvol_shallow_copy(struct spdk_lvol *lvol, const char *bdev_name, spdk_lvol_op_complete cb_fn, void *cb_arg); +/** + * @brief Get a fragmap for a specific segment of a logical volume using the provided offset and size + * + * @param lvol Handle to lvol + * @param offset Offset in bytes of the specific segment of the logical volume + * @param size Size in bytes of the specific segment of the logical volume + * @param cb_fn Completion callback + * @param cb_arg Completion callback custom arguments + */ +void +vbdev_lvol_get_fragmap(struct spdk_lvol *lvol, uint64_t offset, uint64_t size, + spdk_lvol_op_with_fragmap_handle_complete cb_fn, void *cb_arg); + #endif /* SPDK_VBDEV_LVOL_H */ diff --git a/module/bdev/lvol/vbdev_lvol_rpc.c b/module/bdev/lvol/vbdev_lvol_rpc.c index 23fed5445..db610790b 100644 --- a/module/bdev/lvol/vbdev_lvol_rpc.c +++ b/module/bdev/lvol/vbdev_lvol_rpc.c @@ -10,6 +10,8 @@ #include "vbdev_lvol.h" #include "spdk/string.h" #include "spdk/log.h" +#include "spdk/bdev_module.h" +#include "spdk/bit_array.h" SPDK_LOG_REGISTER_COMPONENT(lvol_rpc) @@ -1505,3 +1507,96 @@ cleanup: SPDK_RPC_REGISTER("bdev_lvol_shallow_copy_status", rpc_bdev_lvol_shallow_copy_status, SPDK_RPC_RUNTIME) + +struct rpc_bdev_lvol_get_fragmap { + char *name; + uint64_t offset; + uint64_t size; +}; + +static void +free_rpc_bdev_lvol_get_fragmap(struct rpc_bdev_lvol_get_fragmap *r) +{ + free(r->name); +} + +static const struct spdk_json_object_decoder rpc_bdev_lvol_get_fragmap_decoders[] = { + {"name", offsetof(struct rpc_bdev_lvol_get_fragmap, name), spdk_json_decode_string, true}, + {"offset", offsetof(struct rpc_bdev_lvol_get_fragmap, offset), spdk_json_decode_uint64, true}, + {"size", offsetof(struct rpc_bdev_lvol_get_fragmap, size), spdk_json_decode_uint64, true}, +}; + +static void +rpc_bdev_lvol_get_fragmap_cb(void *cb_arg, struct spdk_fragmap *fragmap, int lvolerrno) +{ + struct spdk_json_write_ctx *w; + struct spdk_jsonrpc_request *request = cb_arg; + char *encoded; + + if (lvolerrno != 0) { + goto invalid; + } + + encoded = spdk_bit_array_to_base64_string(fragmap->map); + if (encoded == NULL) { + SPDK_ERRLOG("Failed to encode fragmap to base64 string\n"); + lvolerrno = -EINVAL; + goto invalid; + } + + w = spdk_jsonrpc_begin_result(request); + spdk_json_write_object_begin(w); + + spdk_json_write_named_uint64(w, "cluster_size", fragmap->cluster_size); + spdk_json_write_named_uint64(w, "num_clusters", fragmap->num_clusters); + spdk_json_write_named_uint64(w, "num_allocated_clusters", fragmap->num_allocated_clusters); + spdk_json_write_named_string(w, "fragmap", encoded); + + spdk_json_write_object_end(w); + spdk_jsonrpc_end_result(request, w); + + free(encoded); + return; + +invalid: + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INVALID_PARAMS, + spdk_strerror(-lvolerrno)); +} + +static void +rpc_bdev_lvol_get_fragmap(struct spdk_jsonrpc_request *request, const struct spdk_json_val *params) +{ + struct rpc_bdev_lvol_get_fragmap req = {}; + struct spdk_bdev *bdev; + struct spdk_lvol *lvol; + + if (spdk_json_decode_object(params, rpc_bdev_lvol_get_fragmap_decoders, + SPDK_COUNTOF(rpc_bdev_lvol_get_fragmap_decoders), + &req)) { + SPDK_ERRLOG("spdk_json_decode_object failed\n"); + spdk_jsonrpc_send_error_response(request, SPDK_JSONRPC_ERROR_INTERNAL_ERROR, + "spdk_json_decode_object failed"); + goto cleanup; + } + + bdev = spdk_bdev_get_by_name(req.name); + if (bdev == NULL) { + SPDK_ERRLOG("bdev '%s' does not exist\n", req.name); + spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); + goto cleanup; + } + + lvol = vbdev_lvol_get_from_bdev(bdev); + if (lvol == NULL) { + SPDK_ERRLOG("lvol does not exist\n"); + spdk_jsonrpc_send_error_response(request, -ENODEV, spdk_strerror(ENODEV)); + goto cleanup; + } + + vbdev_lvol_get_fragmap(lvol, req.offset, req.size, rpc_bdev_lvol_get_fragmap_cb, request); + +cleanup: + free_rpc_bdev_lvol_get_fragmap(&req); +} + +SPDK_RPC_REGISTER("bdev_lvol_get_fragmap", rpc_bdev_lvol_get_fragmap, SPDK_RPC_RUNTIME) diff --git a/python/spdk/rpc/lvol.py b/python/spdk/rpc/lvol.py index 8bcfa8954..7e5376d41 100644 --- a/python/spdk/rpc/lvol.py +++ b/python/spdk/rpc/lvol.py @@ -249,6 +249,25 @@ def bdev_lvol_shallow_copy_status(client, src_lvol_name): return client.call('bdev_lvol_shallow_copy_status', params) +def bdev_lvol_get_fragmap(client, name, offset=0, size=0): + """Get a fragmap for a specific segment of a logical volume using the provided offset and size + + Args: + name: lvol bdev name + offset: offset in bytes of the specific segment of the logical volume + size: size in bytes of the specific segment of the logical volume + """ + params = { + 'name': name, + } + if offset: + params['offset'] = offset + if size: + params['size'] = size + + return client.call('bdev_lvol_get_fragmap', params) + + def bdev_lvol_delete_lvstore(client, uuid=None, lvs_name=None): """Destroy a logical volume store. diff --git a/scripts/rpc.py b/scripts/rpc.py index 9484abaf8..3b2d17534 100755 --- a/scripts/rpc.py +++ b/scripts/rpc.py @@ -2059,6 +2059,17 @@ Format: 'user:u1 secret:s1 muser:mu1 msecret:ms1,user:u2 secret:s2 muser:mu2 mse p.add_argument('src_lvol_name', help='source lvol name') p.set_defaults(func=bdev_lvol_shallow_copy_status) + def bdev_lvol_get_fragmap(args): + print_json(rpc.lvol.bdev_lvol_get_fragmap(args.client, + name=args.name, + offset=args.offset, + size=args.size)) + p = subparsers.add_parser('bdev_lvol_get_fragmap', help='Get a fragmap for a specific segment of a logical volume using the provided offset and size.') + p.add_argument('name', help='lvol bdev name') + p.add_argument('--offset', help='offset in bytes of the specific segment of the logical volume', type=int, required=False) + p.add_argument('--size', help='size in bytes of the specific segment of the logical volume', type=int, required=False) + p.set_defaults(func=bdev_lvol_get_fragmap) + def bdev_lvol_delete_lvstore(args): rpc.lvol.bdev_lvol_delete_lvstore(args.client, uuid=args.uuid, diff --git a/test/lvol/fragmap.sh b/test/lvol/fragmap.sh new file mode 100755 index 000000000..cc9212ab2 --- /dev/null +++ b/test/lvol/fragmap.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (C) 2023 SUSE LLC. +# All rights reserved. +# +testdir=$(readlink -f $(dirname $0)) +rootdir=$(readlink -f $testdir/../..) +source $rootdir/test/common/autotest_common.sh +source $rootdir/test/lvol/common.sh +source $rootdir/test/bdev/nbd_common.sh + +function test_fragmap() { + # Create lvs + bs_malloc_name=$(rpc_cmd bdev_malloc_create 20 $MALLOC_BS) + lvs_uuid=$(rpc_cmd bdev_lvol_create_lvstore "$bs_malloc_name" lvs_test) + + # Create lvol with 4 cluster + lvol_size=$((LVS_DEFAULT_CLUSTER_SIZE_MB * 4)) + lvol_uuid=$(rpc_cmd bdev_lvol_create -u "$lvs_uuid" lvol_test "$lvol_size" -t) + + # Fill second and fourth cluster of lvol + nbd_start_disks "$DEFAULT_RPC_ADDR" "$lvol_uuid" /dev/nbd0 + dd if=/dev/urandom of=/dev/nbd0 oflag=direct bs="$LVS_DEFAULT_CLUSTER_SIZE" count=1 seek=1 + dd if=/dev/urandom of=/dev/nbd0 oflag=direct bs="$LVS_DEFAULT_CLUSTER_SIZE" count=1 seek=3 + nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd0 + + # Create snapshots of lvol bdev + snapshot_uuid=$(rpc_cmd bdev_lvol_snapshot lvs_test/lvol_test lvol_snapshot) + + # Fill first and third cluster of lvol + nbd_start_disks "$DEFAULT_RPC_ADDR" "$lvol_uuid" /dev/nbd0 + dd if=/dev/urandom of=/dev/nbd0 oflag=direct bs="$LVS_DEFAULT_CLUSTER_SIZE" count=1 + dd if=/dev/urandom of=/dev/nbd0 oflag=direct bs="$LVS_DEFAULT_CLUSTER_SIZE" count=1 seek=2 + nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd0 + + + + # Stop nbd devices + nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd1 + nbd_stop_disks "$DEFAULT_RPC_ADDR" /dev/nbd0 +} + +$SPDK_BIN_DIR/spdk_tgt & +spdk_pid=$! +trap 'killprocess "$spdk_pid"; exit 1' SIGINT SIGTERM EXIT +waitforlisten $spdk_pid +modprobe nbd + +run_test "test_shallow_copy_compare" test_shallow_copy_compare + +trap - SIGINT SIGTERM EXIT +killprocess $spdk_pid