diff --git a/include/spdk/event.h b/include/spdk/event.h index 25e22a430..a7971640e 100644 --- a/include/spdk/event.h +++ b/include/spdk/event.h @@ -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. diff --git a/lib/bdev/bdev.c b/lib/bdev/bdev.c index b763ceacc..6b450c6fa 100644 --- a/lib/bdev/bdev.c +++ b/lib/bdev/bdev.c @@ -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) { diff --git a/lib/event/reactor.c b/lib/event/reactor.c index 4cbff1cda..d62d3c701 100644 --- a/lib/event/reactor.c +++ b/lib/event/reactor.c @@ -47,6 +47,7 @@ #endif #include +#include #include #include #include @@ -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 diff --git a/lib/nvmf/subsystem.c b/lib/nvmf/subsystem.c index 00ae3dd35..a67ab89be 100644 --- a/lib/nvmf/subsystem.c +++ b/lib/nvmf/subsystem.c @@ -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); diff --git a/test/lib/event/Makefile b/test/lib/event/Makefile index b0991ffb0..e615cc870 100644 --- a/test/lib/event/Makefile +++ b/test/lib/event/Makefile @@ -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) diff --git a/test/lib/event/event.sh b/test/lib/event/event.sh index 3c2f53d0d..f850ed66d 100755 --- a/test/lib/event/event.sh +++ b/test/lib/event/event.sh @@ -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 diff --git a/test/lib/event/reactor/.gitignore b/test/lib/event/reactor/.gitignore new file mode 100644 index 000000000..194b15d77 --- /dev/null +++ b/test/lib/event/reactor/.gitignore @@ -0,0 +1 @@ +reactor diff --git a/test/lib/event/reactor/Makefile b/test/lib/event/reactor/Makefile new file mode 100644 index 000000000..c583448da --- /dev/null +++ b/test/lib/event/reactor/Makefile @@ -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 diff --git a/test/lib/event/reactor/reactor.c b/test/lib/event/reactor/reactor.c new file mode 100644 index 000000000..22495e817 --- /dev/null +++ b/test/lib/event/reactor/reactor.c @@ -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 +#include +#include +#include + +#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; +} diff --git a/test/lib/nvmf/subsystem/subsystem_ut.c b/test/lib/nvmf/subsystem/subsystem_ut.c index 4ad430447..94746a54f 100644 --- a/test/lib/nvmf/subsystem/subsystem_ut.c +++ b/test/lib/nvmf/subsystem/subsystem_ut.c @@ -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) { }