event: add timer-based pollers

Allow pollers to be scheduled to be run periodically every N
microseconds instead of every iteration of the reactor loop.

Change-Id: Iaea3e98965d81044e6dc5ce5f406bcb7a455289e
Signed-off-by: Daniel Verkamp <daniel.verkamp@intel.com>
This commit is contained in:
Daniel Verkamp 2016-07-26 12:56:07 -07:00 committed by Ben Walker
parent aeb3d50603
commit c41ab41c17
10 changed files with 283 additions and 14 deletions

View File

@ -105,6 +105,8 @@ typedef void (*spdk_poller_fn)(void *arg);
struct spdk_poller {
TAILQ_ENTRY(spdk_poller) tailq;
uint32_t lcore;
uint64_t period_ticks;
uint64_t next_run_tick;
spdk_poller_fn fn;
void *arg;
};
@ -220,7 +222,8 @@ void spdk_event_queue_run_all(uint32_t lcore);
*/
void spdk_poller_register(struct spdk_poller *poller,
uint32_t lcore,
struct spdk_event *complete);
struct spdk_event *complete,
uint64_t period_microseconds);
/**
* \brief Unregister a poller on the given lcore.

View File

@ -448,7 +448,7 @@ spdk_bdev_io_submit(struct spdk_bdev_io *bdev_io)
if (lcore == 0) {
lcore = rte_lcore_id();
}
spdk_poller_register(&bdev->poller, lcore, NULL);
spdk_poller_register(&bdev->poller, lcore, NULL, 0);
}
if (bdev_io->status == SPDK_BDEV_IO_STATUS_PENDING) {

View File

@ -47,6 +47,7 @@
#endif
#include <rte_config.h>
#include <rte_cycles.h>
#include <rte_debug.h>
#include <rte_mempool.h>
#include <rte_ring.h>
@ -68,7 +69,7 @@ enum spdk_reactor_state {
struct spdk_reactor {
/* Logical core number for this reactor. */
uint32_t lcore;
uint32_t lcore;
/*
* Contains pollers actively running on this reactor. Pollers
@ -76,9 +77,14 @@ struct spdk_reactor {
* of the ring, executes it, then puts it back at the tail of
* the ring.
*/
TAILQ_HEAD(, spdk_poller) active_pollers;
TAILQ_HEAD(, spdk_poller) active_pollers;
struct rte_ring *events;
/**
* Contains pollers running on this reactor with a periodic timer.
*/
TAILQ_HEAD(timer_pollers_head, spdk_poller) timer_pollers;
struct rte_ring *events;
};
static struct spdk_reactor g_reactors[RTE_MAX_LCORE];
@ -220,6 +226,30 @@ static void set_reactor_thread_name(void)
#endif
}
static void
spdk_poller_insert_timer(struct spdk_reactor *reactor, struct spdk_poller *poller, uint64_t now)
{
struct spdk_poller *iter;
uint64_t next_run_tick;
next_run_tick = now + poller->period_ticks;
poller->next_run_tick = next_run_tick;
/*
* Insert poller in the reactor's timer_pollers list in sorted order by next scheduled
* run time.
*/
TAILQ_FOREACH_REVERSE(iter, &reactor->timer_pollers, timer_pollers_head, tailq) {
if (iter->next_run_tick <= next_run_tick) {
TAILQ_INSERT_AFTER(&reactor->timer_pollers, iter, poller, tailq);
return;
}
}
/* No earlier pollers were found, so this poller must be the new head */
TAILQ_INSERT_HEAD(&reactor->timer_pollers, poller, tailq);
}
/**
\brief This is the main function of the reactor thread.
@ -270,6 +300,17 @@ _spdk_reactor_run(void *arg)
TAILQ_INSERT_TAIL(&reactor->active_pollers, poller, tailq);
}
poller = TAILQ_FIRST(&reactor->timer_pollers);
if (poller) {
uint64_t now = rte_get_timer_cycles();
if (now >= poller->next_run_tick) {
TAILQ_REMOVE(&reactor->timer_pollers, poller, tailq);
poller->fn(poller->arg);
spdk_poller_insert_timer(reactor, poller, now);
}
}
if (g_reactor_state != SPDK_REACTOR_STATE_RUNNING) {
break;
}
@ -286,6 +327,7 @@ spdk_reactor_construct(struct spdk_reactor *reactor, uint32_t lcore)
reactor->lcore = lcore;
TAILQ_INIT(&reactor->active_pollers);
TAILQ_INIT(&reactor->timer_pollers);
snprintf(ring_name, sizeof(ring_name) - 1, "spdk_event_queue_%u", lcore);
reactor->events =
@ -523,16 +565,20 @@ _spdk_event_add_poller(spdk_event_t event)
poller->lcore = reactor->lcore;
TAILQ_INSERT_TAIL(&reactor->active_pollers, poller, tailq);
if (poller->period_ticks) {
spdk_poller_insert_timer(reactor, poller, rte_get_timer_cycles());
} else {
TAILQ_INSERT_TAIL(&reactor->active_pollers, poller, tailq);
}
if (next) {
spdk_event_call(next);
}
}
void
spdk_poller_register(struct spdk_poller *poller,
uint32_t lcore, spdk_event_t complete)
static void
_spdk_poller_register(struct spdk_poller *poller, uint32_t lcore,
struct spdk_event *complete)
{
struct spdk_reactor *reactor;
struct spdk_event *event;
@ -542,6 +588,19 @@ spdk_poller_register(struct spdk_poller *poller,
spdk_event_call(event);
}
void
spdk_poller_register(struct spdk_poller *poller,
uint32_t lcore, struct spdk_event *complete, uint64_t period_microseconds)
{
if (period_microseconds) {
poller->period_ticks = (rte_get_timer_hz() * period_microseconds) / 1000000ULL;
} else {
poller->period_ticks = 0;
}
_spdk_poller_register(poller, lcore, complete);
}
static void
_spdk_event_remove_poller(spdk_event_t event)
{
@ -549,7 +608,11 @@ _spdk_event_remove_poller(spdk_event_t event)
struct spdk_poller *poller = spdk_event_get_arg2(event);
struct spdk_event *next = spdk_event_get_next(event);
TAILQ_REMOVE(&reactor->active_pollers, poller, tailq);
if (poller->period_ticks) {
TAILQ_REMOVE(&reactor->timer_pollers, poller, tailq);
} else {
TAILQ_REMOVE(&reactor->active_pollers, poller, tailq);
}
if (next) {
spdk_event_call(next);
@ -579,7 +642,7 @@ _spdk_poller_migrate(spdk_event_t event)
* because we already set this event up so that it is called
* on the new_lcore.
*/
spdk_poller_register(poller, rte_lcore_id(), next);
_spdk_poller_register(poller, rte_lcore_id(), next);
}
void

View File

@ -115,7 +115,7 @@ nvmf_create_subsystem(int num, const char *name,
subsystem->poller.fn = spdk_nvmf_subsystem_poller;
subsystem->poller.arg = subsystem;
spdk_poller_register(&subsystem->poller, lcore, NULL);
spdk_poller_register(&subsystem->poller, lcore, NULL, 0);
TAILQ_INSERT_HEAD(&g_subsystems, subsystem, entries);

View File

@ -34,7 +34,7 @@
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../..)
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
DIRS-y = event subsystem
DIRS-y = event reactor subsystem
.PHONY: all clean $(DIRS-y)

View File

@ -6,5 +6,6 @@ source $rootdir/scripts/autotest_common.sh
timing_enter event
$testdir/event/event -m 0xF -t 5
$testdir/reactor/reactor -t 1
$testdir/subsystem/subsystem_ut
timing_exit event

1
test/lib/event/reactor/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
reactor

View File

@ -0,0 +1,57 @@
#
# BSD LICENSE
#
# Copyright (c) Intel Corporation.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Intel Corporation nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
SPDK_ROOT_DIR := $(abspath $(CURDIR)/../../../..)
include $(SPDK_ROOT_DIR)/mk/spdk.common.mk
CFLAGS += $(DPDK_INC)
APP = reactor
C_SRCS := reactor.c
SPDK_LIBS += $(SPDK_ROOT_DIR)/lib/event/libspdk_event.a \
$(SPDK_ROOT_DIR)/lib/trace/libspdk_trace.a \
$(SPDK_ROOT_DIR)/lib/conf/libspdk_conf.a \
$(SPDK_ROOT_DIR)/lib/util/libspdk_util.a \
$(SPDK_ROOT_DIR)/lib/log/libspdk_log.a \
LIBS += $(SPDK_LIBS) $(DPDK_LIB)
all : $(APP)
$(APP) : $(OBJS) $(SPDK_LIBS)
$(LINK_C)
clean :
$(CLEAN_C) $(APP)
include $(SPDK_ROOT_DIR)/mk/spdk.deps.mk

View File

@ -0,0 +1,143 @@
/*-
* BSD LICENSE
*
* Copyright (c) Intel Corporation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "spdk/event.h"
static int g_time_in_sec;
static struct spdk_poller test_end_poller;
static struct spdk_poller poller_100ms;
static struct spdk_poller poller_250ms;
static struct spdk_poller poller_500ms;
static void
test_end(void *arg)
{
printf("test_end\n");
spdk_app_stop(0);
}
static void
tick(void *arg)
{
uintptr_t period = (uintptr_t)arg;
printf("tick %" PRIu64 "\n", (uint64_t)period);
}
static void
test_start(spdk_event_t evt)
{
printf("test_start\n");
/* Register a poller that will stop the test after the time has elapsed. */
test_end_poller.fn = test_end;
spdk_poller_register(&test_end_poller, 0, NULL, g_time_in_sec * 1000000ULL);
poller_100ms.fn = tick;
poller_100ms.arg = (void *)100;
spdk_poller_register(&poller_100ms, 0, NULL, 100000);
poller_250ms.fn = tick;
poller_250ms.arg = (void *)250;
spdk_poller_register(&poller_250ms, 0, NULL, 250000);
poller_500ms.fn = tick;
poller_500ms.arg = (void *)500;
spdk_poller_register(&poller_500ms, 0, NULL, 500000);
}
static void
test_cleanup(void)
{
printf("test_cleanup\n");
spdk_poller_unregister(&test_end_poller, NULL);
spdk_poller_unregister(&poller_100ms, NULL);
spdk_poller_unregister(&poller_250ms, NULL);
spdk_poller_unregister(&poller_500ms, NULL);
}
static void
usage(const char *program_name)
{
printf("%s options\n", program_name);
printf("\t[-t time in seconds]\n");
}
int
main(int argc, char **argv)
{
struct spdk_app_opts opts;
int op;
spdk_app_opts_init(&opts);
opts.name = "reactor";
g_time_in_sec = 0;
while ((op = getopt(argc, argv, "t:")) != -1) {
switch (op) {
case 't':
g_time_in_sec = atoi(optarg);
break;
default:
usage(argv[0]);
exit(1);
}
}
if (!g_time_in_sec) {
usage(argv[0]);
exit(1);
}
optind = 1;
opts.shutdown_cb = test_cleanup;
spdk_app_opts_init(&opts);
spdk_app_init(&opts);
spdk_app_start(test_start, NULL, NULL);
test_cleanup();
spdk_app_fini();
return 0;
}

View File

@ -43,7 +43,8 @@
SPDK_LOG_REGISTER_TRACE_FLAG("nvmf", SPDK_TRACE_NVMF)
void
spdk_poller_register(struct spdk_poller *poller, uint32_t lcore, struct spdk_event *complete)
spdk_poller_register(struct spdk_poller *poller, uint32_t lcore, struct spdk_event *complete,
uint64_t period_microseconds)
{
}