poller: add register interrupt function

Defined callback for spdk_poller to adapt itself to
set interrupt or poll mode. The callback can
be registered to spdk_poller by new function
`spdk_poller_register_interrupt`

Interrupt callback operations for period poller are implemented,
so period pollers now are interruptable.

Change-Id: I2aa6ebfdd75f76b85a70af7e42530be4131ddc8a
Signed-off-by: Liu Xiaodong <xiaodong.liu@intel.com>
Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/5752
Reviewed-by: Konrad Sztyber <konrad.sztyber@intel.com>
Reviewed-by: Ben Walker <benjamin.walker@intel.com>
Reviewed-by: Jim Harris <james.r.harris@intel.com>
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Community-CI: Mellanox Build Bot
This commit is contained in:
Liu Xiaodong 2020-12-30 06:48:29 -05:00 committed by Tomasz Zawadzki
parent 39527e93d8
commit c7cf48ddbe
5 changed files with 143 additions and 35 deletions

View File

@ -54,6 +54,10 @@ Added `spdk_thread_set_interrupt_mode` function in order to set present spdk_the
interrupt mode or back to poll mode. It is valid only when thread interrupt facility is interrupt mode or back to poll mode. It is valid only when thread interrupt facility is
enabled by spdk_interrupt_mode_enable(). enabled by spdk_interrupt_mode_enable().
Added `spdk_poller_register_interrupt` function to mark that the poller is capable of
entering interrupt mode. Callback function will be called when the poller must transition
into or out of interrupt mode.
### iscsi ### iscsi
A security vulnerability has been identified and fixed in the SPDK iSCSI target. A security vulnerability has been identified and fixed in the SPDK iSCSI target.

View File

@ -148,6 +148,30 @@ typedef struct spdk_poller *(*spdk_start_poller)(void *thread_ctx,
*/ */
typedef void (*spdk_stop_poller)(struct spdk_poller *poller, void *thread_ctx); typedef void (*spdk_stop_poller)(struct spdk_poller *poller, void *thread_ctx);
/**
* Callback function to set poller into interrupt mode or back to poll mode.
*
* \param poller Poller to set interrupt or poll mode.
* \param cb_arg Argument passed to the callback function.
* \param interrupt_mode Set interrupt mode for true, or poll mode for false
*/
typedef void (*spdk_poller_set_interrupt_mode_cb)(struct spdk_poller *poller, void *cb_arg,
bool interrupt_mode);
/**
* Mark that the poller is capable of entering interrupt mode.
*
* When registering the poller set interrupt callback, the callback will get
* executed immediately if its spdk_thread is in the interrupt mode.
*
* \param poller The poller to register callback function.
* \param cb_fn Callback function called when the poller must transition into or out of interrupt mode
* \param cb_arg Argument passed to the callback function.
*/
void spdk_poller_register_interrupt(struct spdk_poller *poller,
spdk_poller_set_interrupt_mode_cb cb_fn,
void *cb_arg);
/** /**
* I/O channel creation callback. * I/O channel creation callback.
* *

View File

@ -75,6 +75,8 @@ struct spdk_poller {
void *arg; void *arg;
struct spdk_thread *thread; struct spdk_thread *thread;
int timerfd; int timerfd;
spdk_poller_set_interrupt_mode_cb set_intr_cb_fn;
void *set_intr_cb_arg;
char name[SPDK_MAX_POLLER_NAME_LEN + 1]; char name[SPDK_MAX_POLLER_NAME_LEN + 1];
}; };

View File

@ -35,6 +35,7 @@
spdk_poller_unregister; spdk_poller_unregister;
spdk_poller_pause; spdk_poller_pause;
spdk_poller_resume; spdk_poller_resume;
spdk_poller_register_interrupt;
spdk_io_device_register; spdk_io_device_register;
spdk_io_device_unregister; spdk_io_device_unregister;
spdk_get_io_channel; spdk_get_io_channel;

View File

@ -1010,12 +1010,6 @@ period_poller_interrupt_init(struct spdk_poller *poller)
struct spdk_fd_group *fgrp = poller->thread->fgrp; struct spdk_fd_group *fgrp = poller->thread->fgrp;
int timerfd; int timerfd;
int rc; int rc;
uint64_t now_tick = spdk_get_ticks();
uint64_t ticks = spdk_get_ticks_hz();
int ret;
struct itimerspec new_tv;
assert(poller->period_ticks != 0);
SPDK_DEBUGLOG(thread, "timerfd init for period poller %s\n", poller->name); SPDK_DEBUGLOG(thread, "timerfd init for period poller %s\n", poller->name);
timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
@ -1031,12 +1025,31 @@ period_poller_interrupt_init(struct spdk_poller *poller)
} }
poller->timerfd = timerfd; poller->timerfd = timerfd;
return 0;
}
/* Set repeated timer expirations */ static void
period_poller_set_interrupt_mode(struct spdk_poller *poller, void *cb_arg, bool interrupt_mode)
{
int timerfd = poller->timerfd;
uint64_t now_tick = spdk_get_ticks();
uint64_t ticks = spdk_get_ticks_hz();
int ret;
struct itimerspec new_tv = {};
struct itimerspec old_tv = {};
assert(poller->period_ticks != 0);
assert(timerfd >= 0);
SPDK_DEBUGLOG(thread, "timerfd set poller %s into %s mode\n", poller->name,
interrupt_mode ? "interrupt" : "poll");
if (interrupt_mode) {
/* Set repeated timer expiration */
new_tv.it_interval.tv_sec = poller->period_ticks / ticks; new_tv.it_interval.tv_sec = poller->period_ticks / ticks;
new_tv.it_interval.tv_nsec = poller->period_ticks % ticks * SPDK_SEC_TO_NSEC / ticks; new_tv.it_interval.tv_nsec = poller->period_ticks % ticks * SPDK_SEC_TO_NSEC / ticks;
/* Update next expiration */ /* Update next timer expiration */
if (poller->next_run_tick == 0) { if (poller->next_run_tick == 0) {
poller->next_run_tick = now_tick + poller->period_ticks; poller->next_run_tick = now_tick + poller->period_ticks;
} else if (poller->next_run_tick < now_tick) { } else if (poller->next_run_tick < now_tick) {
@ -1048,13 +1061,26 @@ period_poller_interrupt_init(struct spdk_poller *poller)
ret = timerfd_settime(timerfd, 0, &new_tv, NULL); ret = timerfd_settime(timerfd, 0, &new_tv, NULL);
if (ret < 0) { if (ret < 0) {
rc = -errno;
SPDK_ERRLOG("Failed to arm timerfd: error(%d)\n", errno); SPDK_ERRLOG("Failed to arm timerfd: error(%d)\n", errno);
period_poller_interrupt_fini(poller); assert(false);
return rc; }
} else {
/* Disarm the timer */
ret = timerfd_settime(timerfd, 0, &new_tv, &old_tv);
if (ret < 0) {
/* timerfd_settime's failure indicates that the timerfd is in error */
SPDK_ERRLOG("Failed to disarm timerfd: error(%d)\n", errno);
assert(false);
} }
return 0; /* In order to reuse poller_insert_timer, fix now_tick, so next_run_tick would be
* now_tick + ticks * old_tv.it_value.tv_sec + (ticks * old_tv.it_value.tv_nsec) / SPDK_SEC_TO_NSEC
*/
now_tick = now_tick - poller->period_ticks + ticks * old_tv.it_value.tv_sec + \
(ticks * old_tv.it_value.tv_nsec) / SPDK_SEC_TO_NSEC;
TAILQ_REMOVE(&poller->thread->timed_pollers, poller, tailq);
poller_insert_timer(poller->thread, poller, now_tick);
}
} }
static void static void
@ -1075,12 +1101,39 @@ period_poller_interrupt_init(struct spdk_poller *poller)
return -ENOTSUP; return -ENOTSUP;
} }
static void
period_poller_set_interrupt_mode(struct spdk_poller *poller, void *cb_arg, bool interrupt_mode)
{
}
static void static void
period_poller_interrupt_fini(struct spdk_poller *poller) period_poller_interrupt_fini(struct spdk_poller *poller)
{ {
} }
#endif #endif
void
spdk_poller_register_interrupt(struct spdk_poller *poller,
spdk_poller_set_interrupt_mode_cb cb_fn,
void *cb_arg)
{
assert(poller != NULL);
assert(cb_fn != NULL);
assert(spdk_get_thread() == poller->thread);
if (!spdk_interrupt_mode_is_enabled()) {
return;
}
poller->set_intr_cb_fn = cb_fn;
poller->set_intr_cb_arg = cb_arg;
/* Set poller into interrupt mode if thread is in interrupt. */
if (poller->thread->in_interrupt) {
poller->set_intr_cb_fn(poller, poller->set_intr_cb_arg, true);
}
}
static struct spdk_poller * static struct spdk_poller *
poller_register(spdk_poller_fn fn, poller_register(spdk_poller_fn fn,
void *arg, void *arg,
@ -1139,6 +1192,8 @@ poller_register(spdk_poller_fn fn,
free(poller); free(poller);
return NULL; return NULL;
} }
spdk_poller_register_interrupt(poller, period_poller_set_interrupt_mode, NULL);
} }
thread_insert_poller(thread, poller); thread_insert_poller(thread, poller);
@ -1365,10 +1420,27 @@ spdk_for_each_thread(spdk_msg_fn fn, void *ctx, spdk_msg_fn cpl)
assert(rc == 0); assert(rc == 0);
} }
static inline void
poller_set_interrupt_mode(struct spdk_poller *poller, bool interrupt_mode)
{
if (poller->state == SPDK_POLLER_STATE_UNREGISTERED) {
return;
}
if (!poller->set_intr_cb_fn) {
SPDK_ERRLOG("Poller(%s) doesn't support set interrupt mode.\n", poller->name);
assert(false);
return;
}
poller->set_intr_cb_fn(poller, poller->set_intr_cb_arg, interrupt_mode);
}
void void
spdk_thread_set_interrupt_mode(bool enable_interrupt) spdk_thread_set_interrupt_mode(bool enable_interrupt)
{ {
struct spdk_thread *thread = _get_thread(); struct spdk_thread *thread = _get_thread();
struct spdk_poller *poller, *tmp;
assert(thread); assert(thread);
assert(spdk_interrupt_mode_is_enabled()); assert(spdk_interrupt_mode_is_enabled());
@ -1377,12 +1449,17 @@ spdk_thread_set_interrupt_mode(bool enable_interrupt)
return; return;
} }
/* Currently, only spdk_thread without pollers can set interrupt mode. /* Set pollers to expected mode */
* TODO: remove it after adding interrupt mode switch into poller. TAILQ_FOREACH_SAFE(poller, &thread->timed_pollers, tailq, tmp) {
*/ poller_set_interrupt_mode(poller, enable_interrupt);
assert(TAILQ_EMPTY(&thread->timed_pollers)); }
assert(TAILQ_EMPTY(&thread->active_pollers)); TAILQ_FOREACH_SAFE(poller, &thread->active_pollers, tailq, tmp) {
assert(TAILQ_EMPTY(&thread->paused_pollers)); poller_set_interrupt_mode(poller, enable_interrupt);
}
/* All paused pollers will go to work in interrupt mode */
TAILQ_FOREACH_SAFE(poller, &thread->paused_pollers, tailq, tmp) {
poller_set_interrupt_mode(poller, enable_interrupt);
}
thread->in_interrupt = enable_interrupt; thread->in_interrupt = enable_interrupt;
return; return;