bdev: Add spdk_bdev_unregister_by_name() to handle race condtions

To unregister a bdev more correctly, we had to call
spdk_bdev_open_ext(), spdk_bdev_desc_get_bdev(), spdk_bdev_unregister(),
and then spdk_bdev_close(). This was correct but complicated.

Hence add a new public API spdk_bdev_unregister_by_name() which does
the whole correct sequence of bdev unregistration.

Signed-off-by: Shuhei Matsumoto <smatsumoto@nvidia.com>
Change-Id: I9068d4ac49dca944436e0ba587308fd356dfef75
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/12065
Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com>
Community-CI: Mellanox Build Bot
Reviewed-by: Tomasz Zawadzki <tomasz.zawadzki@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
This commit is contained in:
Shuhei Matsumoto 2022-03-29 12:26:13 +09:00 committed by Tomasz Zawadzki
parent b0aba3fcd5
commit 96c007d301
5 changed files with 97 additions and 2 deletions

View File

@ -12,6 +12,8 @@ fragment copies.
Removed deprecated spdk_bdev_module_finish_done(). Use spdk_bdev_module_fini_done() instead.
A new API `spdk_bdev_unregister_by_name` was added to handle race conditions correctly.
### idxd
A new parameter `flags` was added to all low level submission and preparation

View File

@ -803,16 +803,35 @@ int spdk_bdev_register(struct spdk_bdev *bdev);
/**
* Start unregistering a bdev. This will notify each currently open descriptor
* on this bdev about the hotremoval in hopes that the upper layers will stop
* using this bdev and manually close all the descriptors with spdk_bdev_close().
* on this bdev of the hotremoval to request the upper layers to stop using this bdev
* and manually close all the descriptors with spdk_bdev_close().
* The actual bdev unregistration may be deferred until all descriptors are closed.
*
* Note: spdk_bdev_unregister() can be unsafe unless the bdev is not opened before and
* closed after unregistration. It is recommended to use spdk_bdev_unregister_by_name().
*
* \param bdev Block device to unregister.
* \param cb_fn Callback function to be called when the unregister is complete.
* \param cb_arg Argument to be supplied to cb_fn
*/
void spdk_bdev_unregister(struct spdk_bdev *bdev, spdk_bdev_unregister_cb cb_fn, void *cb_arg);
/**
* Start unregistering a bdev. This will notify each currently open descriptor
* on this bdev of the hotremoval to request the upper layer to stop using this bdev
* and manually close all the descriptors with spdk_bdev_close().
* The actual bdev unregistration may be deferred until all descriptors are closed.
*
* \param bdev_name Block device name to unregister.
* \param module Module by which the block device was registered.
* \param cb_fn Callback function to be called when the unregister is complete.
* \param cb_arg Argument to be supplied to cb_fn
*
* \return 0 on success, or suitable errno value otherwise
*/
int spdk_bdev_unregister_by_name(const char *bdev_name, struct spdk_bdev_module *module,
spdk_bdev_unregister_cb cb_fn, void *cb_arg);
/**
* Invokes the unregister callback of a bdev backing a virtual bdev.
*

View File

@ -6145,6 +6145,42 @@ spdk_bdev_unregister(struct spdk_bdev *bdev, spdk_bdev_unregister_cb cb_fn, void
}
}
static void
_tmp_bdev_event_cb(enum spdk_bdev_event_type type, struct spdk_bdev *bdev, void *ctx)
{
SPDK_NOTICELOG("Unexpected event type: %d\n", type);
}
int
spdk_bdev_unregister_by_name(const char *bdev_name, struct spdk_bdev_module *module,
spdk_bdev_unregister_cb cb_fn, void *cb_arg)
{
struct spdk_bdev_desc *desc;
struct spdk_bdev *bdev;
int rc;
rc = spdk_bdev_open_ext(bdev_name, false, _tmp_bdev_event_cb, NULL, &desc);
if (rc != 0) {
SPDK_ERRLOG("Failed to open bdev with name: %s\n", bdev_name);
return rc;
}
bdev = spdk_bdev_desc_get_bdev(desc);
if (bdev->module != module) {
spdk_bdev_close(desc);
SPDK_ERRLOG("Bdev %s was not registered by the specified module.\n",
bdev_name);
return -ENODEV;
}
spdk_bdev_unregister(bdev, cb_fn, cb_arg);
spdk_bdev_close(desc);
return 0;
}
static int
bdev_start_qos(struct spdk_bdev *bdev)
{

View File

@ -101,6 +101,7 @@
# Public functions in bdev_module.h
spdk_bdev_register;
spdk_bdev_unregister;
spdk_bdev_unregister_by_name;
spdk_bdev_destruct_done;
spdk_bdev_module_examine_done;
spdk_bdev_module_init_done;

View File

@ -5153,6 +5153,42 @@ bdev_register_uuid_alias(void)
poll_threads();
}
static void
bdev_unregister_by_name(void)
{
struct spdk_bdev *bdev;
int rc;
bdev = allocate_bdev("bdev");
g_event_type1 = 0xFF;
g_unregister_arg = NULL;
g_unregister_rc = -1;
rc = spdk_bdev_unregister_by_name("bdev1", &bdev_ut_if, bdev_unregister_cb, (void *)0x12345678);
CU_ASSERT(rc == -ENODEV);
rc = spdk_bdev_unregister_by_name("bdev", &vbdev_ut_if, bdev_unregister_cb, (void *)0x12345678);
CU_ASSERT(rc == -ENODEV);
rc = spdk_bdev_unregister_by_name("bdev", &bdev_ut_if, bdev_unregister_cb, (void *)0x12345678);
CU_ASSERT(rc == 0);
/* Check that unregister callback is delayed */
CU_ASSERT(g_unregister_arg == NULL);
CU_ASSERT(g_unregister_rc == -1);
poll_threads();
/* Event callback shall not be issued because device was closed */
CU_ASSERT(g_event_type1 == 0xFF);
/* Unregister callback is issued */
CU_ASSERT(g_unregister_arg == (void *)0x12345678);
CU_ASSERT(g_unregister_rc == 0);
free_bdev(bdev);
}
int
main(int argc, char **argv)
{
@ -5202,6 +5238,7 @@ main(int argc, char **argv)
CU_ADD_TEST(suite, bdev_get_memory_domains);
CU_ADD_TEST(suite, bdev_writev_readv_ext);
CU_ADD_TEST(suite, bdev_register_uuid_alias);
CU_ADD_TEST(suite, bdev_unregister_by_name);
allocate_cores(1);
allocate_threads(1);