diff --git a/include/spdk/blob.h b/include/spdk/blob.h index 1c56e1681..545f5b81c 100644 --- a/include/spdk/blob.h +++ b/include/spdk/blob.h @@ -428,6 +428,34 @@ uint64_t spdk_blob_get_num_io_units(struct spdk_blob *blob); */ uint64_t spdk_blob_get_num_clusters(struct spdk_blob *blob); +/** + * Get next allocated io_unit + * + * Starting at 'offset' io_units into the blob, returns the offset of + * the first allocated io unit found. + * If 'offset' points to an allocated io_unit, same offset is returned. + * + * \param blob Blob struct to query. + * \param offset Offset is in io units from the beginning of the blob. + * + * \return offset in io_units or UINT64_MAX if no allocated io_unit found + */ +uint64_t spdk_blob_get_next_allocated_io_unit(struct spdk_blob *blob, uint64_t offset); + +/** + * Get next unallocated io_unit + * + * Starting at 'offset' io_units into the blob, returns the offset of + * the first unallocated io unit found. + * If 'offset' points to an unallocated io_unit, same offset is returned. + * + * \param blob Blob struct to query. + * \param offset Offset is in io units from the beginning of the blob. + * + * \return offset in io_units or UINT64_MAX if only allocated io_unit found + */ +uint64_t spdk_blob_get_next_unallocated_io_unit(struct spdk_blob *blob, uint64_t offset); + struct spdk_blob_xattr_opts { /* Number of attributes */ size_t count; diff --git a/lib/blob/blobstore.c b/lib/blob/blobstore.c index 61cb9871d..e28bb911c 100644 --- a/lib/blob/blobstore.c +++ b/lib/blob/blobstore.c @@ -5593,6 +5593,34 @@ spdk_blob_get_num_clusters(struct spdk_blob *blob) return blob->active.num_clusters; } +static uint64_t +blob_find_io_unit(struct spdk_blob *blob, uint64_t offset, bool is_allocated) +{ + uint64_t blob_io_unit_num = spdk_blob_get_num_io_units(blob); + + while (offset < blob_io_unit_num) { + if (bs_io_unit_is_allocated(blob, offset) == is_allocated) { + return offset; + } + + offset += bs_num_io_units_to_cluster_boundary(blob, offset); + } + + return UINT64_MAX; +} + +uint64_t +spdk_blob_get_next_allocated_io_unit(struct spdk_blob *blob, uint64_t offset) +{ + return blob_find_io_unit(blob, offset, true); +} + +uint64_t +spdk_blob_get_next_unallocated_io_unit(struct spdk_blob *blob, uint64_t offset) +{ + return blob_find_io_unit(blob, offset, false); +} + /* START spdk_bs_create_blob */ static void diff --git a/lib/blob/spdk_blob.map b/lib/blob/spdk_blob.map index b9fa3f439..4324abc62 100644 --- a/lib/blob/spdk_blob.map +++ b/lib/blob/spdk_blob.map @@ -20,6 +20,8 @@ spdk_blob_get_num_pages; spdk_blob_get_num_io_units; spdk_blob_get_num_clusters; + spdk_blob_get_next_allocated_io_unit; + spdk_blob_get_next_unallocated_io_unit; spdk_blob_opts_init; spdk_bs_create_blob_ext; spdk_bs_create_blob; diff --git a/test/unit/lib/blob/blob.c/blob_ut.c b/test/unit/lib/blob/blob.c/blob_ut.c index c7c61fe95..87658ada7 100644 --- a/test/unit/lib/blob/blob.c/blob_ut.c +++ b/test/unit/lib/blob/blob.c/blob_ut.c @@ -7316,6 +7316,82 @@ blob_decouple_snapshot(void) } } +static void +blob_seek_io_unit(void) +{ + struct spdk_blob_store *bs = g_bs; + struct spdk_blob *blob; + struct spdk_io_channel *channel; + struct spdk_blob_opts opts; + uint64_t free_clusters; + uint8_t payload[10 * 4096]; + uint64_t offset; + uint64_t io_unit, io_units_per_cluster; + + free_clusters = spdk_bs_free_cluster_count(bs); + + channel = spdk_bs_alloc_io_channel(bs); + CU_ASSERT(channel != NULL); + + /* Set blob as thin provisioned */ + ut_spdk_blob_opts_init(&opts); + opts.thin_provision = true; + + /* Create a blob */ + blob = ut_blob_create_and_open(bs, &opts); + CU_ASSERT(free_clusters == spdk_bs_free_cluster_count(bs)); + + io_units_per_cluster = bs_io_units_per_cluster(blob); + + /* The blob started at 0 clusters. Resize it to be 5, but still unallocated. */ + spdk_blob_resize(blob, 5, blob_op_complete, NULL); + poll_threads(); + CU_ASSERT(g_bserrno == 0); + CU_ASSERT(free_clusters == spdk_bs_free_cluster_count(bs)); + CU_ASSERT(blob->active.num_clusters == 5); + + /* Write at the beginning of first cluster */ + offset = 0; + spdk_blob_io_write(blob, channel, payload, offset, 1, blob_op_complete, NULL); + poll_threads(); + CU_ASSERT(g_bserrno == 0); + + io_unit = spdk_blob_get_next_allocated_io_unit(blob, 0); + CU_ASSERT(io_unit == offset); + + io_unit = spdk_blob_get_next_unallocated_io_unit(blob, 0); + CU_ASSERT(io_unit == io_units_per_cluster); + + /* Write in the middle of third cluster */ + offset = 2 * io_units_per_cluster + io_units_per_cluster / 2; + spdk_blob_io_write(blob, channel, payload, offset, 1, blob_op_complete, NULL); + poll_threads(); + CU_ASSERT(g_bserrno == 0); + + io_unit = spdk_blob_get_next_allocated_io_unit(blob, io_units_per_cluster); + CU_ASSERT(io_unit == 2 * io_units_per_cluster); + + io_unit = spdk_blob_get_next_unallocated_io_unit(blob, 2 * io_units_per_cluster); + CU_ASSERT(io_unit == 3 * io_units_per_cluster); + + /* Write at the end of last cluster */ + offset = 5 * io_units_per_cluster - 1; + spdk_blob_io_write(blob, channel, payload, offset, 1, blob_op_complete, NULL); + poll_threads(); + CU_ASSERT(g_bserrno == 0); + + io_unit = spdk_blob_get_next_allocated_io_unit(blob, 3 * io_units_per_cluster); + CU_ASSERT(io_unit == 4 * io_units_per_cluster); + + io_unit = spdk_blob_get_next_unallocated_io_unit(blob, 4 * io_units_per_cluster); + CU_ASSERT(io_unit == UINT64_MAX); + + spdk_bs_free_io_channel(channel); + poll_threads(); + + ut_blob_close_and_delete(bs, blob); +} + static void suite_bs_setup(void) { @@ -7491,6 +7567,7 @@ main(int argc, char **argv) CU_ADD_TEST(suite_bs, blob_simultaneous_operations); CU_ADD_TEST(suite_bs, blob_persist_test); CU_ADD_TEST(suite_bs, blob_decouple_snapshot); + CU_ADD_TEST(suite_bs, blob_seek_io_unit); allocate_threads(2); set_thread(0);