nvmf: pause/resume polling for the target

There is a way to pause/resume spdk pollers, however there is no way
to achieve that using public API for the given target which has
a hook behaving similar to pollers. Exposing such functionality can
be used for pausing and restoring target pollers during
reset, e.g. new commands should not be fetched to assure
that all internal resources can be cleared/reinitialized safety.
Pausing target poller during the reset will assure that, without
need for destroying transport or adding condition statements in IO path.

Similar use case might be hitless upgrade. Depending on implementation
there might be need that no new command can be submitted when
secondary processes are being switched to upgraded versions.
Pausing target pollers should be useful in this case.

Signed-off-by: Kamuda Szymon <szymon.kamuda@intel.com>
Change-Id: I419816552c710c43e02197ebcc20a967fb23b3bd
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/15911
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Konrad Sztyber <konrad.sztyber@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
This commit is contained in:
Kamuda, Szymon 2022-12-13 15:16:32 +01:00 committed by Tomasz Zawadzki
parent 3359bf34d6
commit cb2f0a2cf5
4 changed files with 181 additions and 0 deletions

View File

@ -1071,6 +1071,46 @@ void spdk_nvmf_tgt_add_transport(struct spdk_nvmf_tgt *tgt,
spdk_nvmf_tgt_add_transport_done_fn cb_fn,
void *cb_arg);
/**
* Function to be called once target pause is complete.
*
* \param cb_arg Callback argument passed to this function.
* \param status 0 if it completed successfully, or negative errno if it failed.
*/
typedef void (*spdk_nvmf_tgt_pause_polling_cb_fn)(void *cb_arg, int status);
/**
* Pause polling on the given target.
*
* \param tgt The target to pause
* \param cb_fn A callback that will be called once the target is paused
* \param cb_arg A context argument passed to cb_fn.
*
* \return 0 if it completed successfully, or negative errno if it failed.
*/
int spdk_nvmf_tgt_pause_polling(struct spdk_nvmf_tgt *tgt, spdk_nvmf_tgt_pause_polling_cb_fn cb_fn,
void *cb_arg);
/**
* Function to be called once target resume is complete.
*
* \param cb_arg Callback argument passed to this function.
* \param status 0 if it completed successfully, or negative errno if it failed.
*/
typedef void (*spdk_nvmf_tgt_resume_polling_cb_fn)(void *cb_arg, int status);
/**
* Resume polling on the given target.
*
* \param tgt The target to resume
* \param cb_fn A callback that will be called once the target is resumed
* \param cb_arg A context argument passed to cb_fn.
*
* \return 0 if it completed successfully, or negative errno if it failed.
*/
int spdk_nvmf_tgt_resume_polling(struct spdk_nvmf_tgt *tgt,
spdk_nvmf_tgt_resume_polling_cb_fn cb_fn, void *cb_arg);
/**
* Add listener to transport and begin accepting new connections.
*

View File

@ -133,6 +133,7 @@ nvmf_tgt_destroy_poll_group(void *io_device, void *ctx_buf)
TAILQ_REMOVE(&tgt->poll_groups, group, link);
pthread_mutex_unlock(&tgt->mutex);
assert(!(tgt->state == NVMF_TGT_PAUSING || tgt->state == NVMF_TGT_RESUMING));
nvmf_tgt_cleanup_poll_group(group);
}
@ -321,6 +322,8 @@ spdk_nvmf_tgt_create(struct spdk_nvmf_target_opts *opts)
sizeof(struct spdk_nvmf_poll_group),
tgt->name);
tgt->state = NVMF_TGT_RUNNING;
TAILQ_INSERT_HEAD(&g_nvmf_tgts, tgt, link);
return tgt;
@ -384,6 +387,8 @@ spdk_nvmf_tgt_destroy(struct spdk_nvmf_tgt *tgt,
spdk_nvmf_tgt_destroy_done_fn cb_fn,
void *cb_arg)
{
assert(!(tgt->state == NVMF_TGT_PAUSING || tgt->state == NVMF_TGT_RESUMING));
tgt->destroy_cb_fn = cb_fn;
tgt->destroy_cb_arg = cb_arg;
@ -795,6 +800,130 @@ spdk_nvmf_tgt_add_transport(struct spdk_nvmf_tgt *tgt,
_nvmf_tgt_add_transport_done);
}
struct nvmf_tgt_pause_ctx {
struct spdk_nvmf_tgt *tgt;
spdk_nvmf_tgt_pause_polling_cb_fn cb_fn;
void *cb_arg;
};
static void
_nvmf_tgt_pause_polling_done(struct spdk_io_channel_iter *i, int status)
{
struct nvmf_tgt_pause_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
ctx->tgt->state = NVMF_TGT_PAUSED;
ctx->cb_fn(ctx->cb_arg, status);
free(ctx);
}
static void
_nvmf_tgt_pause_polling(struct spdk_io_channel_iter *i)
{
struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(i);
struct spdk_nvmf_poll_group *group = spdk_io_channel_get_ctx(ch);
spdk_poller_unregister(&group->poller);
spdk_for_each_channel_continue(i, 0);
}
int
spdk_nvmf_tgt_pause_polling(struct spdk_nvmf_tgt *tgt, spdk_nvmf_tgt_pause_polling_cb_fn cb_fn,
void *cb_arg)
{
struct nvmf_tgt_pause_ctx *ctx;
SPDK_DTRACE_PROBE2(nvmf_tgt_pause_polling, tgt, tgt->name);
switch (tgt->state) {
case NVMF_TGT_PAUSING:
case NVMF_TGT_RESUMING:
return -EBUSY;
case NVMF_TGT_RUNNING:
break;
default:
return -EINVAL;
}
ctx = calloc(1, sizeof(*ctx));
if (!ctx) {
return -ENOMEM;
}
tgt->state = NVMF_TGT_PAUSING;
ctx->tgt = tgt;
ctx->cb_fn = cb_fn;
ctx->cb_arg = cb_arg;
spdk_for_each_channel(tgt,
_nvmf_tgt_pause_polling,
ctx,
_nvmf_tgt_pause_polling_done);
return 0;
}
static void
_nvmf_tgt_resume_polling_done(struct spdk_io_channel_iter *i, int status)
{
struct nvmf_tgt_pause_ctx *ctx = spdk_io_channel_iter_get_ctx(i);
ctx->tgt->state = NVMF_TGT_RUNNING;
ctx->cb_fn(ctx->cb_arg, status);
free(ctx);
}
static void
_nvmf_tgt_resume_polling(struct spdk_io_channel_iter *i)
{
struct spdk_io_channel *ch = spdk_io_channel_iter_get_channel(i);
struct spdk_nvmf_poll_group *group = spdk_io_channel_get_ctx(ch);
assert(group->poller == NULL);
group->poller = SPDK_POLLER_REGISTER(nvmf_poll_group_poll, group, 0);
spdk_for_each_channel_continue(i, 0);
}
int
spdk_nvmf_tgt_resume_polling(struct spdk_nvmf_tgt *tgt, spdk_nvmf_tgt_resume_polling_cb_fn cb_fn,
void *cb_arg)
{
struct nvmf_tgt_pause_ctx *ctx;
SPDK_DTRACE_PROBE2(nvmf_tgt_resume_polling, tgt, tgt->name);
switch (tgt->state) {
case NVMF_TGT_PAUSING:
case NVMF_TGT_RESUMING:
return -EBUSY;
case NVMF_TGT_PAUSED:
break;
default:
return -EINVAL;
}
ctx = calloc(1, sizeof(*ctx));
if (!ctx) {
return -ENOMEM;
}
tgt->state = NVMF_TGT_RESUMING;
ctx->tgt = tgt;
ctx->cb_fn = cb_fn;
ctx->cb_arg = cb_arg;
spdk_for_each_channel(tgt,
_nvmf_tgt_resume_polling,
ctx,
_nvmf_tgt_resume_polling_done);
return 0;
}
struct spdk_nvmf_subsystem *
spdk_nvmf_tgt_find_subsystem(struct spdk_nvmf_tgt *tgt, const char *subnqn)
{

View File

@ -24,6 +24,14 @@
#define NVMF_MIN_CNTLID 1
#define NVMF_MAX_CNTLID 0xFFEF
enum spdk_nvmf_tgt_state {
NVMF_TGT_IDLE = 0,
NVMF_TGT_RUNNING,
NVMF_TGT_PAUSING,
NVMF_TGT_PAUSED,
NVMF_TGT_RESUMING,
};
enum spdk_nvmf_subsystem_state {
SPDK_NVMF_SUBSYSTEM_INACTIVE = 0,
SPDK_NVMF_SUBSYSTEM_ACTIVATING,
@ -46,6 +54,8 @@ struct spdk_nvmf_tgt {
enum spdk_nvmf_tgt_discovery_filter discovery_filter;
enum spdk_nvmf_tgt_state state;
/* Array of subsystem pointers of size max_subsystems indexed by sid */
struct spdk_nvmf_subsystem **subsystems;

View File

@ -75,6 +75,8 @@
spdk_nvmf_get_transport_type;
spdk_nvmf_get_transport_name;
spdk_nvmf_tgt_add_transport;
spdk_nvmf_tgt_pause_polling;
spdk_nvmf_tgt_resume_polling;
spdk_nvmf_transport_listen;
spdk_nvmf_transport_stop_listen;
spdk_nvmf_transport_stop_listen_async;