Commit 9e386832 authored by Artur Paszkiewicz's avatar Artur Paszkiewicz Committed by Jim Harris
Browse files

bdev: add quiesce API



This is simply a wrapper of the bdev lba range locking functions.

Change-Id: Idad345ae6cf1bc3e02985eaaacb8bca1ba4dae22
Signed-off-by: default avatarArtur Paszkiewicz <artur.paszkiewicz@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/17890


Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Community-CI: Mellanox Build Bot
Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarJim Harris <james.r.harris@intel.com>
parent 687cfd4b
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -13,6 +13,10 @@ function requires a bdev descriptor to be passed and the claim is automatically
descriptor is closed. It allows bdev modules to claim bdevs as a single writer, multiple writers, or
multiple readers.

New APIs `spdk_bdev_quiesce`, `spdk_bdev_unquiesce`, `spdk_bdev_quiesce_range` and
`spdk_bdev_unquiesce_range` were added. These allow I/O to be quiesced on an entire bdev or
a specified LBA range.

### env

New functions `spdk_env_get_main_core` and `spdk_env_get_cpuset` were added.
+72 −0
Original line number Diff line number Diff line
@@ -1682,6 +1682,78 @@ enum spdk_bdev_reset_stat_mode {
 */
void spdk_bdev_reset_io_stat(struct spdk_bdev_io_stat *stat, enum spdk_bdev_reset_stat_mode mode);

typedef void (*spdk_bdev_quiesce_cb)(void *ctx, int status);

/**
 * Quiesce a bdev. All I/O submitted after this function is called will be queued until
 * the bdev is unquiesced. A callback will be called when all outstanding I/O on this bdev
 * submitted before calling this function have completed.
 *
 * Only the module that registered the bdev may call this function.
 *
 * \param bdev Block device.
 * \param module The module that registered the bdev.
 * \param cb_fn Callback function to be called when the bdev is quiesced. Optional.
 * \param cb_arg Argument to be supplied to cb_fn.
 *
 * \return 0 on success, or suitable errno value otherwise.
 */
int spdk_bdev_quiesce(struct spdk_bdev *bdev, struct spdk_bdev_module *module,
		      spdk_bdev_quiesce_cb cb_fn, void *cb_arg);

/**
 * Unquiesce a previously quiesced bdev. All I/O queued after the bdev was quiesced
 * will be submitted.
 *
 * Only the module that registered the bdev may call this function.
 *
 * \param bdev Block device.
 * \param module The module that registered the bdev.
 * \param cb_fn Callback function to be called when the bdev is unquiesced. Optional.
 * \param cb_arg Argument to be supplied to cb_fn.
 *
 * \return 0 on success, or suitable errno value otherwise.
 */
int spdk_bdev_unquiesce(struct spdk_bdev *bdev, struct spdk_bdev_module *module,
			spdk_bdev_quiesce_cb cb_fn, void *cb_arg);

/**
 * Quiesce a bdev LBA range.
 * Same as spdk_bdev_quiesce() but limited to the specified LBA range.
 *
 * \param bdev Block device.
 * \param module The module that registered the bdev.
 * \param offset The offset of the start of the range, in blocks,
 *               from the start of the block device.
 * \param length The length of the range, in blocks.
 * \param cb_fn Callback function to be called when the range is quiesced. Optional.
 * \param cb_arg Argument to be supplied to cb_fn.
 *
 * \return 0 on success, or suitable errno value otherwise.
 */
int spdk_bdev_quiesce_range(struct spdk_bdev *bdev, struct spdk_bdev_module *module,
			    uint64_t offset, uint64_t length,
			    spdk_bdev_quiesce_cb cb_fn, void *cb_arg);

/**
 * Unquiesce a previously quiesced bdev LBA range.
 * Same as spdk_bdev_unquiesce() but limited to the specified LBA range.
 * The specified range must match exactly a previously quiesced LBA range.
 *
 * \param bdev Block device.
 * \param module The module that registered the bdev.
 * \param offset The offset of the start of the range, in blocks,
 *               from the start of the block device.
 * \param length The length of the range, in blocks.
 * \param cb_fn Callback function to be called when the range is unquiesced. Optional.
 * \param cb_arg Argument to be supplied to cb_fn.
 *
 * \return 0 on success, or suitable errno value otherwise.
 */
int spdk_bdev_unquiesce_range(struct spdk_bdev *bdev, struct spdk_bdev_module *module,
			      uint64_t offset, uint64_t length,
			      spdk_bdev_quiesce_cb cb_fn, void *cb_arg);

/*
 *  Macro used to register module for later initialization.
 */
+86 −0
Original line number Diff line number Diff line
@@ -9505,6 +9505,92 @@ bdev_unlock_lba_range(struct spdk_bdev_desc *desc, struct spdk_io_channel *_ch,
	return _bdev_unlock_lba_range(bdev, offset, length, cb_fn, cb_arg);
}

struct bdev_quiesce_ctx {
	spdk_bdev_quiesce_cb cb_fn;
	void *cb_arg;
};

static void
bdev_quiesce_lock_range_cb(struct lba_range *range, void *ctx, int status)
{
	struct bdev_quiesce_ctx *quiesce_ctx = ctx;

	if (quiesce_ctx->cb_fn != NULL) {
		quiesce_ctx->cb_fn(quiesce_ctx->cb_arg, status);
	}

	free(quiesce_ctx);
}

static int
_spdk_bdev_quiesce(struct spdk_bdev *bdev, struct spdk_bdev_module *module,
		   uint64_t offset, uint64_t length,
		   spdk_bdev_quiesce_cb cb_fn, void *cb_arg,
		   bool unquiesce)
{
	struct bdev_quiesce_ctx *quiesce_ctx;
	int rc;

	if (module != bdev->module) {
		SPDK_ERRLOG("Bdev does not belong to specified module.\n");
		return -EINVAL;
	}

	if (!bdev_io_valid_blocks(bdev, offset, length)) {
		return -EINVAL;
	}

	quiesce_ctx = malloc(sizeof(*quiesce_ctx));
	if (quiesce_ctx == NULL) {
		return -ENOMEM;
	}

	quiesce_ctx->cb_fn = cb_fn;
	quiesce_ctx->cb_arg = cb_arg;

	if (unquiesce) {
		rc = _bdev_unlock_lba_range(bdev, offset, length, bdev_quiesce_lock_range_cb, quiesce_ctx);
	} else {
		rc = _bdev_lock_lba_range(bdev, NULL, offset, length, bdev_quiesce_lock_range_cb, quiesce_ctx);
	}

	if (rc != 0) {
		free(quiesce_ctx);
	}

	return rc;
}

int
spdk_bdev_quiesce(struct spdk_bdev *bdev, struct spdk_bdev_module *module,
		  spdk_bdev_quiesce_cb cb_fn, void *cb_arg)
{
	return _spdk_bdev_quiesce(bdev, module, 0, bdev->blockcnt, cb_fn, cb_arg, false);
}

int
spdk_bdev_unquiesce(struct spdk_bdev *bdev, struct spdk_bdev_module *module,
		    spdk_bdev_quiesce_cb cb_fn, void *cb_arg)
{
	return _spdk_bdev_quiesce(bdev, module, 0, bdev->blockcnt, cb_fn, cb_arg, true);
}

int
spdk_bdev_quiesce_range(struct spdk_bdev *bdev, struct spdk_bdev_module *module,
			uint64_t offset, uint64_t length,
			spdk_bdev_quiesce_cb cb_fn, void *cb_arg)
{
	return _spdk_bdev_quiesce(bdev, module, offset, length, cb_fn, cb_arg, false);
}

int
spdk_bdev_unquiesce_range(struct spdk_bdev *bdev, struct spdk_bdev_module *module,
			  uint64_t offset, uint64_t length,
			  spdk_bdev_quiesce_cb cb_fn, void *cb_arg)
{
	return _spdk_bdev_quiesce(bdev, module, offset, length, cb_fn, cb_arg, true);
}

int
spdk_bdev_get_memory_domains(struct spdk_bdev *bdev, struct spdk_memory_domain **domains,
			     int array_size)
+4 −0
Original line number Diff line number Diff line
@@ -167,6 +167,10 @@
	spdk_bdev_reset_io_stat;
	spdk_bdev_add_io_stat;
	spdk_bdev_dump_io_stat_json;
	spdk_bdev_quiesce;
	spdk_bdev_unquiesce;
	spdk_bdev_quiesce_range;
	spdk_bdev_unquiesce_range;

	# Public functions in bdev_zone.h
	spdk_bdev_get_zone_size;
+84 −0
Original line number Diff line number Diff line
@@ -5036,6 +5036,89 @@ lock_lba_range_overlapped(void)
	ut_fini_bdev();
}

static void
bdev_quiesce_done(void *ctx, int status)
{
	g_lock_lba_range_done = true;
}

static void
bdev_unquiesce_done(void *ctx, int status)
{
	g_unlock_lba_range_done = true;
}

static void
bdev_quiesce(void)
{
	struct spdk_bdev *bdev;
	struct spdk_bdev_desc *desc = NULL;
	struct spdk_io_channel *io_ch;
	struct spdk_bdev_channel *channel;
	struct lba_range *range;
	int ctx1;
	int rc;

	ut_init_bdev(NULL);
	bdev = allocate_bdev("bdev0");

	rc = spdk_bdev_open_ext("bdev0", true, bdev_ut_event_cb, NULL, &desc);
	CU_ASSERT(rc == 0);
	CU_ASSERT(desc != NULL);
	CU_ASSERT(bdev == spdk_bdev_desc_get_bdev(desc));
	io_ch = spdk_bdev_get_io_channel(desc);
	CU_ASSERT(io_ch != NULL);
	channel = spdk_io_channel_get_ctx(io_ch);

	g_lock_lba_range_done = false;
	rc = spdk_bdev_quiesce(bdev, &bdev_ut_if, bdev_quiesce_done, &ctx1);
	CU_ASSERT(rc == 0);
	poll_threads();

	CU_ASSERT(g_lock_lba_range_done == true);
	range = TAILQ_FIRST(&channel->locked_ranges);
	SPDK_CU_ASSERT_FATAL(range != NULL);
	CU_ASSERT(range->offset == 0);
	CU_ASSERT(range->length == bdev->blockcnt);
	CU_ASSERT(range->owner_ch == NULL);

	g_unlock_lba_range_done = false;
	rc = spdk_bdev_unquiesce(bdev, &bdev_ut_if, bdev_unquiesce_done, &ctx1);
	CU_ASSERT(rc == 0);
	spdk_delay_us(100);
	poll_threads();

	CU_ASSERT(g_unlock_lba_range_done == true);
	CU_ASSERT(TAILQ_EMPTY(&channel->locked_ranges));

	g_lock_lba_range_done = false;
	rc = spdk_bdev_quiesce_range(bdev, &bdev_ut_if, 20, 10, bdev_quiesce_done, &ctx1);
	CU_ASSERT(rc == 0);
	poll_threads();

	CU_ASSERT(g_lock_lba_range_done == true);
	range = TAILQ_FIRST(&channel->locked_ranges);
	SPDK_CU_ASSERT_FATAL(range != NULL);
	CU_ASSERT(range->offset == 20);
	CU_ASSERT(range->length == 10);
	CU_ASSERT(range->owner_ch == NULL);

	/* Unlocks must exactly match a lock. */
	g_unlock_lba_range_done = false;
	rc = spdk_bdev_unquiesce_range(bdev, &bdev_ut_if, 20, 10, bdev_unquiesce_done, &ctx1);
	CU_ASSERT(rc == 0);
	spdk_delay_us(100);
	poll_threads();

	CU_ASSERT(g_unlock_lba_range_done == true);
	CU_ASSERT(TAILQ_EMPTY(&channel->locked_ranges));

	spdk_put_io_channel(io_ch);
	spdk_bdev_close(desc);
	free_bdev(bdev);
	ut_fini_bdev();
}

static void
abort_done(struct spdk_bdev_io *bdev_io, bool success, void *cb_arg)
{
@@ -7167,6 +7250,7 @@ main(int argc, char **argv)
	CU_ADD_TEST(suite, lock_lba_range_check_ranges);
	CU_ADD_TEST(suite, lock_lba_range_with_io_outstanding);
	CU_ADD_TEST(suite, lock_lba_range_overlapped);
	CU_ADD_TEST(suite, bdev_quiesce);
	CU_ADD_TEST(suite, bdev_io_abort);
	CU_ADD_TEST(suite, bdev_unmap);
	CU_ADD_TEST(suite, bdev_write_zeroes_split_test);