2017-12-19 15:49:03 +00:00
|
|
|
/* SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
* Copyright(c) 2010-2014 Intel Corporation
|
2012-09-04 12:54:00 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdint.h>
|
2019-04-15 21:41:27 +00:00
|
|
|
#include <stdbool.h>
|
2012-09-04 12:54:00 +00:00
|
|
|
#include <inttypes.h>
|
2015-02-17 02:08:16 +00:00
|
|
|
#include <assert.h>
|
2015-03-04 21:50:03 +00:00
|
|
|
#include <sys/queue.h>
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
#include <rte_atomic.h>
|
|
|
|
#include <rte_common.h>
|
|
|
|
#include <rte_cycles.h>
|
2019-07-05 17:22:44 +00:00
|
|
|
#include <rte_eal_memconfig.h>
|
2012-09-04 12:54:00 +00:00
|
|
|
#include <rte_per_lcore.h>
|
|
|
|
#include <rte_memory.h>
|
|
|
|
#include <rte_launch.h>
|
|
|
|
#include <rte_eal.h>
|
|
|
|
#include <rte_lcore.h>
|
|
|
|
#include <rte_branch_prediction.h>
|
|
|
|
#include <rte_spinlock.h>
|
2013-09-18 10:00:00 +00:00
|
|
|
#include <rte_random.h>
|
2017-06-05 08:58:43 +00:00
|
|
|
#include <rte_pause.h>
|
2019-04-15 21:41:27 +00:00
|
|
|
#include <rte_memzone.h>
|
|
|
|
#include <rte_malloc.h>
|
|
|
|
#include <rte_compat.h>
|
2019-05-09 19:39:36 +00:00
|
|
|
#include <rte_errno.h>
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
#include "rte_timer.h"
|
|
|
|
|
2019-04-15 21:41:27 +00:00
|
|
|
/**
|
|
|
|
* Per-lcore info for timers.
|
|
|
|
*/
|
2012-09-04 12:54:00 +00:00
|
|
|
struct priv_timer {
|
2013-09-18 10:00:00 +00:00
|
|
|
struct rte_timer pending_head; /**< dummy timer instance to head up list */
|
2012-09-04 12:54:00 +00:00
|
|
|
rte_spinlock_t list_lock; /**< lock to protect list access */
|
|
|
|
|
|
|
|
/** per-core variable that true if a timer was updated on this
|
|
|
|
* core since last reset of the variable */
|
|
|
|
int updated;
|
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/** track the current depth of the skiplist */
|
|
|
|
unsigned curr_skiplist_depth;
|
|
|
|
|
2012-09-04 12:54:00 +00:00
|
|
|
unsigned prev_lcore; /**< used for lcore round robin */
|
|
|
|
|
2016-07-17 18:08:00 +00:00
|
|
|
/** running timer on this lcore now */
|
|
|
|
struct rte_timer *running_tim;
|
|
|
|
|
2012-09-04 12:54:00 +00:00
|
|
|
#ifdef RTE_LIBRTE_TIMER_DEBUG
|
|
|
|
/** per-lcore statistics */
|
|
|
|
struct rte_timer_debug_stats stats;
|
|
|
|
#endif
|
|
|
|
} __rte_cache_aligned;
|
|
|
|
|
2019-04-15 21:41:27 +00:00
|
|
|
#define FL_ALLOCATED (1 << 0)
|
|
|
|
struct rte_timer_data {
|
|
|
|
struct priv_timer priv_timer[RTE_MAX_LCORE];
|
|
|
|
uint8_t internal_flags;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define RTE_MAX_DATA_ELS 64
|
2019-07-05 17:22:44 +00:00
|
|
|
static const struct rte_memzone *rte_timer_data_mz;
|
|
|
|
static int *volatile rte_timer_mz_refcnt;
|
2019-04-15 21:41:27 +00:00
|
|
|
static struct rte_timer_data *rte_timer_data_arr;
|
|
|
|
static const uint32_t default_data_id;
|
|
|
|
static uint32_t rte_timer_subsystem_initialized;
|
|
|
|
|
|
|
|
/* For maintaining older interfaces for a period */
|
|
|
|
static struct rte_timer_data default_timer_data;
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
/* when debug is enabled, store some statistics */
|
|
|
|
#ifdef RTE_LIBRTE_TIMER_DEBUG
|
2019-04-15 21:41:27 +00:00
|
|
|
#define __TIMER_STAT_ADD(priv_timer, name, n) do { \
|
2015-02-17 02:08:16 +00:00
|
|
|
unsigned __lcore_id = rte_lcore_id(); \
|
|
|
|
if (__lcore_id < RTE_MAX_LCORE) \
|
|
|
|
priv_timer[__lcore_id].stats.name += (n); \
|
2012-09-04 12:54:00 +00:00
|
|
|
} while(0)
|
|
|
|
#else
|
2019-04-15 21:41:27 +00:00
|
|
|
#define __TIMER_STAT_ADD(priv_timer, name, n) do {} while (0)
|
2012-09-04 12:54:00 +00:00
|
|
|
#endif
|
|
|
|
|
2019-04-15 21:41:27 +00:00
|
|
|
static inline int
|
|
|
|
timer_data_valid(uint32_t id)
|
|
|
|
{
|
2019-07-15 15:39:31 +00:00
|
|
|
return rte_timer_data_arr &&
|
|
|
|
(rte_timer_data_arr[id].internal_flags & FL_ALLOCATED);
|
2019-04-15 21:41:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* validate ID and retrieve timer data pointer, or return error value */
|
|
|
|
#define TIMER_DATA_VALID_GET_OR_ERR_RET(id, timer_data, retval) do { \
|
|
|
|
if (id >= RTE_MAX_DATA_ELS || !timer_data_valid(id)) \
|
|
|
|
return retval; \
|
|
|
|
timer_data = &rte_timer_data_arr[id]; \
|
|
|
|
} while (0)
|
|
|
|
|
2019-06-29 11:58:52 +00:00
|
|
|
int
|
2019-04-15 21:41:27 +00:00
|
|
|
rte_timer_data_alloc(uint32_t *id_ptr)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct rte_timer_data *data;
|
|
|
|
|
|
|
|
if (!rte_timer_subsystem_initialized)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
for (i = 0; i < RTE_MAX_DATA_ELS; i++) {
|
|
|
|
data = &rte_timer_data_arr[i];
|
|
|
|
if (!(data->internal_flags & FL_ALLOCATED)) {
|
|
|
|
data->internal_flags |= FL_ALLOCATED;
|
|
|
|
|
|
|
|
if (id_ptr)
|
|
|
|
*id_ptr = i;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOSPC;
|
|
|
|
}
|
|
|
|
|
2019-06-29 11:58:52 +00:00
|
|
|
int
|
2019-04-15 21:41:27 +00:00
|
|
|
rte_timer_data_dealloc(uint32_t id)
|
|
|
|
{
|
|
|
|
struct rte_timer_data *timer_data;
|
|
|
|
TIMER_DATA_VALID_GET_OR_ERR_RET(id, timer_data, -EINVAL);
|
|
|
|
|
|
|
|
timer_data->internal_flags &= ~(FL_ALLOCATED);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-04 12:54:00 +00:00
|
|
|
void
|
2019-04-15 21:41:27 +00:00
|
|
|
rte_timer_subsystem_init_v20(void)
|
2012-09-04 12:54:00 +00:00
|
|
|
{
|
|
|
|
unsigned lcore_id;
|
2019-04-15 21:41:27 +00:00
|
|
|
struct priv_timer *priv_timer = default_timer_data.priv_timer;
|
2012-09-04 12:54:00 +00:00
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/* since priv_timer is static, it's zeroed by default, so only init some
|
|
|
|
* fields.
|
|
|
|
*/
|
2012-09-04 12:54:00 +00:00
|
|
|
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id ++) {
|
|
|
|
rte_spinlock_init(&priv_timer[lcore_id].list_lock);
|
|
|
|
priv_timer[lcore_id].prev_lcore = lcore_id;
|
|
|
|
}
|
|
|
|
}
|
2019-04-15 21:41:27 +00:00
|
|
|
VERSION_SYMBOL(rte_timer_subsystem_init, _v20, 2.0);
|
|
|
|
|
|
|
|
/* Init the timer library. Allocate an array of timer data structs in shared
|
|
|
|
* memory, and allocate the zeroth entry for use with original timer
|
|
|
|
* APIs. Since the intersection of the sets of lcore ids in primary and
|
|
|
|
* secondary processes should be empty, the zeroth entry can be shared by
|
|
|
|
* multiple processes.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
rte_timer_subsystem_init_v1905(void)
|
|
|
|
{
|
|
|
|
const struct rte_memzone *mz;
|
|
|
|
struct rte_timer_data *data;
|
|
|
|
int i, lcore_id;
|
|
|
|
static const char *mz_name = "rte_timer_mz";
|
2019-05-09 19:39:36 +00:00
|
|
|
const size_t data_arr_size =
|
2019-07-05 17:22:44 +00:00
|
|
|
RTE_MAX_DATA_ELS * sizeof(*rte_timer_data_arr);
|
|
|
|
const size_t mem_size = data_arr_size + sizeof(*rte_timer_mz_refcnt);
|
2019-05-09 19:39:36 +00:00
|
|
|
bool do_full_init = true;
|
2019-04-15 21:41:27 +00:00
|
|
|
|
|
|
|
if (rte_timer_subsystem_initialized)
|
|
|
|
return -EALREADY;
|
|
|
|
|
2019-07-05 17:22:44 +00:00
|
|
|
rte_mcfg_timer_lock();
|
2019-05-09 19:39:36 +00:00
|
|
|
|
2019-07-05 17:22:44 +00:00
|
|
|
mz = rte_memzone_lookup(mz_name);
|
|
|
|
if (mz == NULL) {
|
|
|
|
mz = rte_memzone_reserve_aligned(mz_name, mem_size,
|
|
|
|
SOCKET_ID_ANY, 0, RTE_CACHE_LINE_SIZE);
|
|
|
|
if (mz == NULL) {
|
|
|
|
rte_mcfg_timer_unlock();
|
2019-05-09 19:39:36 +00:00
|
|
|
return -ENOMEM;
|
2019-07-05 17:22:44 +00:00
|
|
|
}
|
|
|
|
do_full_init = true;
|
|
|
|
} else
|
|
|
|
do_full_init = false;
|
2019-04-15 21:41:27 +00:00
|
|
|
|
2019-07-05 17:22:44 +00:00
|
|
|
rte_timer_data_mz = mz;
|
2019-04-15 21:41:27 +00:00
|
|
|
rte_timer_data_arr = mz->addr;
|
2019-07-05 17:22:44 +00:00
|
|
|
rte_timer_mz_refcnt = (void *)((char *)mz->addr + data_arr_size);
|
2019-04-15 21:41:27 +00:00
|
|
|
|
2019-05-09 19:39:36 +00:00
|
|
|
if (do_full_init) {
|
|
|
|
for (i = 0; i < RTE_MAX_DATA_ELS; i++) {
|
|
|
|
data = &rte_timer_data_arr[i];
|
|
|
|
|
|
|
|
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE;
|
|
|
|
lcore_id++) {
|
|
|
|
rte_spinlock_init(
|
|
|
|
&data->priv_timer[lcore_id].list_lock);
|
|
|
|
data->priv_timer[lcore_id].prev_lcore =
|
|
|
|
lcore_id;
|
|
|
|
}
|
2019-04-15 21:41:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rte_timer_data_arr[default_data_id].internal_flags |= FL_ALLOCATED;
|
2019-07-05 17:22:44 +00:00
|
|
|
(*rte_timer_mz_refcnt)++;
|
|
|
|
|
|
|
|
rte_mcfg_timer_unlock();
|
2019-04-15 21:41:27 +00:00
|
|
|
|
|
|
|
rte_timer_subsystem_initialized = 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
MAP_STATIC_SYMBOL(int rte_timer_subsystem_init(void),
|
|
|
|
rte_timer_subsystem_init_v1905);
|
|
|
|
BIND_DEFAULT_SYMBOL(rte_timer_subsystem_init, _v1905, 19.05);
|
|
|
|
|
2019-06-29 11:58:52 +00:00
|
|
|
void
|
2019-04-15 21:41:27 +00:00
|
|
|
rte_timer_subsystem_finalize(void)
|
|
|
|
{
|
2019-05-09 19:39:36 +00:00
|
|
|
if (!rte_timer_subsystem_initialized)
|
|
|
|
return;
|
2019-04-15 21:41:27 +00:00
|
|
|
|
2019-07-05 17:22:44 +00:00
|
|
|
rte_mcfg_timer_lock();
|
|
|
|
|
|
|
|
if (--(*rte_timer_mz_refcnt) == 0)
|
|
|
|
rte_memzone_free(rte_timer_data_mz);
|
|
|
|
|
|
|
|
rte_mcfg_timer_unlock();
|
|
|
|
|
2019-04-15 21:41:27 +00:00
|
|
|
rte_timer_subsystem_initialized = 0;
|
|
|
|
}
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
/* Initialize the timer handle tim for use */
|
|
|
|
void
|
|
|
|
rte_timer_init(struct rte_timer *tim)
|
|
|
|
{
|
|
|
|
union rte_timer_status status;
|
|
|
|
|
|
|
|
status.state = RTE_TIMER_STOP;
|
|
|
|
status.owner = RTE_TIMER_NO_OWNER;
|
|
|
|
tim->status.u32 = status.u32;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if timer is pending or stopped (or running on the same core than
|
|
|
|
* us), mark timer as configuring, and on success return the previous
|
|
|
|
* status of the timer
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
timer_set_config_state(struct rte_timer *tim,
|
2019-04-15 21:41:27 +00:00
|
|
|
union rte_timer_status *ret_prev_status,
|
|
|
|
struct priv_timer *priv_timer)
|
2012-09-04 12:54:00 +00:00
|
|
|
{
|
|
|
|
union rte_timer_status prev_status, status;
|
|
|
|
int success = 0;
|
|
|
|
unsigned lcore_id;
|
|
|
|
|
|
|
|
lcore_id = rte_lcore_id();
|
|
|
|
|
|
|
|
/* wait that the timer is in correct status before update,
|
2013-09-18 10:00:00 +00:00
|
|
|
* and mark it as being configured */
|
2012-09-04 12:54:00 +00:00
|
|
|
while (success == 0) {
|
|
|
|
prev_status.u32 = tim->status.u32;
|
|
|
|
|
2016-07-17 18:08:00 +00:00
|
|
|
/* timer is running on another core
|
|
|
|
* or ready to run on local core, exit
|
|
|
|
*/
|
2012-09-04 12:54:00 +00:00
|
|
|
if (prev_status.state == RTE_TIMER_RUNNING &&
|
2016-07-17 18:08:00 +00:00
|
|
|
(prev_status.owner != (uint16_t)lcore_id ||
|
|
|
|
tim != priv_timer[lcore_id].running_tim))
|
2012-09-04 12:54:00 +00:00
|
|
|
return -1;
|
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/* timer is being configured on another core */
|
2012-09-04 12:54:00 +00:00
|
|
|
if (prev_status.state == RTE_TIMER_CONFIG)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* here, we know that timer is stopped or pending,
|
2013-09-18 10:00:00 +00:00
|
|
|
* mark it atomically as being configured */
|
2012-09-04 12:54:00 +00:00
|
|
|
status.state = RTE_TIMER_CONFIG;
|
|
|
|
status.owner = (int16_t)lcore_id;
|
|
|
|
success = rte_atomic32_cmpset(&tim->status.u32,
|
|
|
|
prev_status.u32,
|
|
|
|
status.u32);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret_prev_status->u32 = prev_status.u32;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* if timer is pending, mark timer as running
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
timer_set_running_state(struct rte_timer *tim)
|
|
|
|
{
|
|
|
|
union rte_timer_status prev_status, status;
|
|
|
|
unsigned lcore_id = rte_lcore_id();
|
|
|
|
int success = 0;
|
|
|
|
|
|
|
|
/* wait that the timer is in correct status before update,
|
|
|
|
* and mark it as running */
|
|
|
|
while (success == 0) {
|
|
|
|
prev_status.u32 = tim->status.u32;
|
|
|
|
|
|
|
|
/* timer is not pending anymore */
|
|
|
|
if (prev_status.state != RTE_TIMER_PENDING)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
/* here, we know that timer is stopped or pending,
|
2017-06-07 05:05:06 +00:00
|
|
|
* mark it atomically as being configured */
|
2012-09-04 12:54:00 +00:00
|
|
|
status.state = RTE_TIMER_RUNNING;
|
|
|
|
status.owner = (int16_t)lcore_id;
|
|
|
|
success = rte_atomic32_cmpset(&tim->status.u32,
|
|
|
|
prev_status.u32,
|
|
|
|
status.u32);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/*
|
|
|
|
* Return a skiplist level for a new entry.
|
2017-11-10 08:24:23 +00:00
|
|
|
* This probabilistically gives a level with p=1/4 that an entry at level n
|
2013-09-18 10:00:00 +00:00
|
|
|
* will also appear at level n+1.
|
|
|
|
*/
|
|
|
|
static uint32_t
|
|
|
|
timer_get_skiplist_level(unsigned curr_depth)
|
|
|
|
{
|
|
|
|
#ifdef RTE_LIBRTE_TIMER_DEBUG
|
|
|
|
static uint32_t i, count = 0;
|
|
|
|
static uint32_t levels[MAX_SKIPLIST_DEPTH] = {0};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* probability value is 1/4, i.e. all at level 0, 1 in 4 is at level 1,
|
|
|
|
* 1 in 16 at level 2, 1 in 64 at level 3, etc. Calculated using lowest
|
|
|
|
* bit position of a (pseudo)random number.
|
|
|
|
*/
|
|
|
|
uint32_t rand = rte_rand() & (UINT32_MAX - 1);
|
|
|
|
uint32_t level = rand == 0 ? MAX_SKIPLIST_DEPTH : (rte_bsf32(rand)-1) / 2;
|
|
|
|
|
|
|
|
/* limit the levels used to one above our current level, so we don't,
|
|
|
|
* for instance, have a level 0 and a level 7 without anything between
|
|
|
|
*/
|
|
|
|
if (level > curr_depth)
|
|
|
|
level = curr_depth;
|
|
|
|
if (level >= MAX_SKIPLIST_DEPTH)
|
|
|
|
level = MAX_SKIPLIST_DEPTH-1;
|
|
|
|
#ifdef RTE_LIBRTE_TIMER_DEBUG
|
|
|
|
count ++;
|
|
|
|
levels[level]++;
|
|
|
|
if (count % 10000 == 0)
|
|
|
|
for (i = 0; i < MAX_SKIPLIST_DEPTH; i++)
|
|
|
|
printf("Level %u: %u\n", (unsigned)i, (unsigned)levels[i]);
|
|
|
|
#endif
|
|
|
|
return level;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For a given time value, get the entries at each level which
|
|
|
|
* are <= that time value.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
timer_get_prev_entries(uint64_t time_val, unsigned tim_lcore,
|
2019-04-15 21:41:27 +00:00
|
|
|
struct rte_timer **prev, struct priv_timer *priv_timer)
|
2013-09-18 10:00:00 +00:00
|
|
|
{
|
|
|
|
unsigned lvl = priv_timer[tim_lcore].curr_skiplist_depth;
|
|
|
|
prev[lvl] = &priv_timer[tim_lcore].pending_head;
|
|
|
|
while(lvl != 0) {
|
|
|
|
lvl--;
|
|
|
|
prev[lvl] = prev[lvl+1];
|
|
|
|
while (prev[lvl]->sl_next[lvl] &&
|
|
|
|
prev[lvl]->sl_next[lvl]->expire <= time_val)
|
|
|
|
prev[lvl] = prev[lvl]->sl_next[lvl];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given a timer node in the skiplist, find the previous entries for it at
|
|
|
|
* all skiplist levels.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
timer_get_prev_entries_for_node(struct rte_timer *tim, unsigned tim_lcore,
|
2019-04-15 21:41:27 +00:00
|
|
|
struct rte_timer **prev,
|
|
|
|
struct priv_timer *priv_timer)
|
2013-09-18 10:00:00 +00:00
|
|
|
{
|
|
|
|
int i;
|
2019-04-15 21:41:27 +00:00
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/* to get a specific entry in the list, look for just lower than the time
|
|
|
|
* values, and then increment on each level individually if necessary
|
|
|
|
*/
|
2019-04-15 21:41:27 +00:00
|
|
|
timer_get_prev_entries(tim->expire - 1, tim_lcore, prev, priv_timer);
|
2013-09-18 10:00:00 +00:00
|
|
|
for (i = priv_timer[tim_lcore].curr_skiplist_depth - 1; i >= 0; i--) {
|
|
|
|
while (prev[i]->sl_next[i] != NULL &&
|
|
|
|
prev[i]->sl_next[i] != tim &&
|
|
|
|
prev[i]->sl_next[i]->expire <= tim->expire)
|
|
|
|
prev[i] = prev[i]->sl_next[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
timer: fix race condition
rte_timer_manage() adds expired timers to a "run list", and walks the
list, transitioning each timer from the PENDING to the RUNNING state.
If another lcore resets or stops the timer at precisely this
moment, the timer state would instead be set to CONFIG by that other
lcore, which would cause timer_manage() to skip over it. This is
expected behavior.
However, if a timer expires quickly enough, there exists the
following race condition that causes the timer_manage() routine to
misinterpret a timer in CONFIG state, resulting in lost timers:
- Thread A:
- starts a timer with rte_timer_reset()
- the timer is moved to CONFIG state
- the spinlock associated with the appropriate skiplist is acquired
- timer is inserted into the skiplist
- the spinlock is released
- Thread B:
- executes rte_timer_manage()
- find above timer as expired, add it to run list
- walk run list, see above timer still in CONFIG state, unlink it from
run list and continue on
- Thread A:
- move timer to PENDING state
- return from rte_timer_reset()
- timer is now in PENDING state, but not actually linked into a
pending list or a run list and will never get processed further
by rte_timer_manage()
This commit fixes this race condition by only releasing the spinlock
after the timer state has been transitioned from CONFIG to PENDING,
which prevents rte_timer_manage() from seeing an incorrect state.
Fixes: 9b15ba895b9f ("timer: use a skip list")
Cc: stable@dpdk.org
Signed-off-by: Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
2018-12-19 16:09:34 +00:00
|
|
|
/* call with lock held as necessary
|
|
|
|
* add in list
|
2012-09-04 12:54:00 +00:00
|
|
|
* timer must be in config state
|
|
|
|
* timer must not be in a list
|
|
|
|
*/
|
|
|
|
static void
|
2019-04-15 21:41:27 +00:00
|
|
|
timer_add(struct rte_timer *tim, unsigned int tim_lcore,
|
|
|
|
struct priv_timer *priv_timer)
|
2012-09-04 12:54:00 +00:00
|
|
|
{
|
2013-09-18 10:00:00 +00:00
|
|
|
unsigned lvl;
|
|
|
|
struct rte_timer *prev[MAX_SKIPLIST_DEPTH+1];
|
2012-09-04 12:54:00 +00:00
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/* find where exactly this element goes in the list of elements
|
|
|
|
* for each depth. */
|
2019-04-15 21:41:27 +00:00
|
|
|
timer_get_prev_entries(tim->expire, tim_lcore, prev, priv_timer);
|
2013-09-18 10:00:00 +00:00
|
|
|
|
|
|
|
/* now assign it a new level and add at that level */
|
|
|
|
const unsigned tim_level = timer_get_skiplist_level(
|
|
|
|
priv_timer[tim_lcore].curr_skiplist_depth);
|
|
|
|
if (tim_level == priv_timer[tim_lcore].curr_skiplist_depth)
|
|
|
|
priv_timer[tim_lcore].curr_skiplist_depth++;
|
|
|
|
|
|
|
|
lvl = tim_level;
|
|
|
|
while (lvl > 0) {
|
|
|
|
tim->sl_next[lvl] = prev[lvl]->sl_next[lvl];
|
|
|
|
prev[lvl]->sl_next[lvl] = tim;
|
|
|
|
lvl--;
|
2012-09-04 12:54:00 +00:00
|
|
|
}
|
2013-09-18 10:00:00 +00:00
|
|
|
tim->sl_next[0] = prev[0]->sl_next[0];
|
|
|
|
prev[0]->sl_next[0] = tim;
|
2012-09-04 12:54:00 +00:00
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/* save the lowest list entry into the expire field of the dummy hdr
|
|
|
|
* NOTE: this is not atomic on 32-bit*/
|
|
|
|
priv_timer[tim_lcore].pending_head.expire = priv_timer[tim_lcore].\
|
|
|
|
pending_head.sl_next[0]->expire;
|
2012-09-04 12:54:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* del from list, lock if needed
|
|
|
|
* timer must be in config state
|
|
|
|
* timer must be in a list
|
|
|
|
*/
|
|
|
|
static void
|
2013-09-18 10:00:00 +00:00
|
|
|
timer_del(struct rte_timer *tim, union rte_timer_status prev_status,
|
2019-04-15 21:41:27 +00:00
|
|
|
int local_is_locked, struct priv_timer *priv_timer)
|
2012-09-04 12:54:00 +00:00
|
|
|
{
|
|
|
|
unsigned lcore_id = rte_lcore_id();
|
2013-09-18 10:00:00 +00:00
|
|
|
unsigned prev_owner = prev_status.owner;
|
|
|
|
int i;
|
|
|
|
struct rte_timer *prev[MAX_SKIPLIST_DEPTH+1];
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
/* if timer needs is pending another core, we need to lock the
|
|
|
|
* list; if it is on local core, we need to lock if we are not
|
|
|
|
* called from rte_timer_manage() */
|
|
|
|
if (prev_owner != lcore_id || !local_is_locked)
|
|
|
|
rte_spinlock_lock(&priv_timer[prev_owner].list_lock);
|
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/* save the lowest list entry into the expire field of the dummy hdr.
|
|
|
|
* NOTE: this is not atomic on 32-bit */
|
|
|
|
if (tim == priv_timer[prev_owner].pending_head.sl_next[0])
|
|
|
|
priv_timer[prev_owner].pending_head.expire =
|
|
|
|
((tim->sl_next[0] == NULL) ? 0 : tim->sl_next[0]->expire);
|
|
|
|
|
|
|
|
/* adjust pointers from previous entries to point past this */
|
2019-04-15 21:41:27 +00:00
|
|
|
timer_get_prev_entries_for_node(tim, prev_owner, prev, priv_timer);
|
2013-09-18 10:00:00 +00:00
|
|
|
for (i = priv_timer[prev_owner].curr_skiplist_depth - 1; i >= 0; i--) {
|
|
|
|
if (prev[i]->sl_next[i] == tim)
|
|
|
|
prev[i]->sl_next[i] = tim->sl_next[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* in case we deleted last entry at a level, adjust down max level */
|
|
|
|
for (i = priv_timer[prev_owner].curr_skiplist_depth - 1; i >= 0; i--)
|
|
|
|
if (priv_timer[prev_owner].pending_head.sl_next[i] == NULL)
|
|
|
|
priv_timer[prev_owner].curr_skiplist_depth --;
|
|
|
|
else
|
|
|
|
break;
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
if (prev_owner != lcore_id || !local_is_locked)
|
|
|
|
rte_spinlock_unlock(&priv_timer[prev_owner].list_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset and start the timer associated with the timer handle (private func) */
|
|
|
|
static int
|
|
|
|
__rte_timer_reset(struct rte_timer *tim, uint64_t expire,
|
|
|
|
uint64_t period, unsigned tim_lcore,
|
|
|
|
rte_timer_cb_t fct, void *arg,
|
2019-04-15 21:41:27 +00:00
|
|
|
int local_is_locked,
|
|
|
|
struct rte_timer_data *timer_data)
|
2012-09-04 12:54:00 +00:00
|
|
|
{
|
|
|
|
union rte_timer_status prev_status, status;
|
|
|
|
int ret;
|
|
|
|
unsigned lcore_id = rte_lcore_id();
|
2019-04-15 21:41:27 +00:00
|
|
|
struct priv_timer *priv_timer = timer_data->priv_timer;
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
/* round robin for tim_lcore */
|
|
|
|
if (tim_lcore == (unsigned)LCORE_ID_ANY) {
|
2015-02-17 02:08:16 +00:00
|
|
|
if (lcore_id < RTE_MAX_LCORE) {
|
|
|
|
/* EAL thread with valid lcore_id */
|
|
|
|
tim_lcore = rte_get_next_lcore(
|
|
|
|
priv_timer[lcore_id].prev_lcore,
|
|
|
|
0, 1);
|
|
|
|
priv_timer[lcore_id].prev_lcore = tim_lcore;
|
|
|
|
} else
|
|
|
|
/* non-EAL thread do not run rte_timer_manage(),
|
|
|
|
* so schedule the timer on the first enabled lcore. */
|
|
|
|
tim_lcore = rte_get_next_lcore(LCORE_ID_ANY, 0, 1);
|
2012-09-04 12:54:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* wait that the timer is in correct status before update,
|
2013-09-18 10:00:00 +00:00
|
|
|
* and mark it as being configured */
|
2019-04-15 21:41:27 +00:00
|
|
|
ret = timer_set_config_state(tim, &prev_status, priv_timer);
|
2012-09-04 12:54:00 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-04-15 21:41:27 +00:00
|
|
|
__TIMER_STAT_ADD(priv_timer, reset, 1);
|
2015-02-17 02:08:16 +00:00
|
|
|
if (prev_status.state == RTE_TIMER_RUNNING &&
|
|
|
|
lcore_id < RTE_MAX_LCORE) {
|
2014-05-21 19:53:45 +00:00
|
|
|
priv_timer[lcore_id].updated = 1;
|
|
|
|
}
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
/* remove it from list */
|
2013-09-18 10:00:00 +00:00
|
|
|
if (prev_status.state == RTE_TIMER_PENDING) {
|
2019-04-15 21:41:27 +00:00
|
|
|
timer_del(tim, prev_status, local_is_locked, priv_timer);
|
|
|
|
__TIMER_STAT_ADD(priv_timer, pending, -1);
|
2012-09-04 12:54:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
tim->period = period;
|
|
|
|
tim->expire = expire;
|
|
|
|
tim->f = fct;
|
|
|
|
tim->arg = arg;
|
|
|
|
|
timer: fix race condition
rte_timer_manage() adds expired timers to a "run list", and walks the
list, transitioning each timer from the PENDING to the RUNNING state.
If another lcore resets or stops the timer at precisely this
moment, the timer state would instead be set to CONFIG by that other
lcore, which would cause timer_manage() to skip over it. This is
expected behavior.
However, if a timer expires quickly enough, there exists the
following race condition that causes the timer_manage() routine to
misinterpret a timer in CONFIG state, resulting in lost timers:
- Thread A:
- starts a timer with rte_timer_reset()
- the timer is moved to CONFIG state
- the spinlock associated with the appropriate skiplist is acquired
- timer is inserted into the skiplist
- the spinlock is released
- Thread B:
- executes rte_timer_manage()
- find above timer as expired, add it to run list
- walk run list, see above timer still in CONFIG state, unlink it from
run list and continue on
- Thread A:
- move timer to PENDING state
- return from rte_timer_reset()
- timer is now in PENDING state, but not actually linked into a
pending list or a run list and will never get processed further
by rte_timer_manage()
This commit fixes this race condition by only releasing the spinlock
after the timer state has been transitioned from CONFIG to PENDING,
which prevents rte_timer_manage() from seeing an incorrect state.
Fixes: 9b15ba895b9f ("timer: use a skip list")
Cc: stable@dpdk.org
Signed-off-by: Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
2018-12-19 16:09:34 +00:00
|
|
|
/* if timer needs to be scheduled on another core, we need to
|
|
|
|
* lock the destination list; if it is on local core, we need to lock if
|
|
|
|
* we are not called from rte_timer_manage()
|
|
|
|
*/
|
|
|
|
if (tim_lcore != lcore_id || !local_is_locked)
|
|
|
|
rte_spinlock_lock(&priv_timer[tim_lcore].list_lock);
|
|
|
|
|
2019-04-15 21:41:27 +00:00
|
|
|
__TIMER_STAT_ADD(priv_timer, pending, 1);
|
|
|
|
timer_add(tim, tim_lcore, priv_timer);
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
/* update state: as we are in CONFIG state, only us can modify
|
|
|
|
* the state so we don't need to use cmpset() here */
|
|
|
|
rte_wmb();
|
|
|
|
status.state = RTE_TIMER_PENDING;
|
|
|
|
status.owner = (int16_t)tim_lcore;
|
|
|
|
tim->status.u32 = status.u32;
|
|
|
|
|
timer: fix race condition
rte_timer_manage() adds expired timers to a "run list", and walks the
list, transitioning each timer from the PENDING to the RUNNING state.
If another lcore resets or stops the timer at precisely this
moment, the timer state would instead be set to CONFIG by that other
lcore, which would cause timer_manage() to skip over it. This is
expected behavior.
However, if a timer expires quickly enough, there exists the
following race condition that causes the timer_manage() routine to
misinterpret a timer in CONFIG state, resulting in lost timers:
- Thread A:
- starts a timer with rte_timer_reset()
- the timer is moved to CONFIG state
- the spinlock associated with the appropriate skiplist is acquired
- timer is inserted into the skiplist
- the spinlock is released
- Thread B:
- executes rte_timer_manage()
- find above timer as expired, add it to run list
- walk run list, see above timer still in CONFIG state, unlink it from
run list and continue on
- Thread A:
- move timer to PENDING state
- return from rte_timer_reset()
- timer is now in PENDING state, but not actually linked into a
pending list or a run list and will never get processed further
by rte_timer_manage()
This commit fixes this race condition by only releasing the spinlock
after the timer state has been transitioned from CONFIG to PENDING,
which prevents rte_timer_manage() from seeing an incorrect state.
Fixes: 9b15ba895b9f ("timer: use a skip list")
Cc: stable@dpdk.org
Signed-off-by: Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
Reviewed-by: Gavin Hu <gavin.hu@arm.com>
2018-12-19 16:09:34 +00:00
|
|
|
if (tim_lcore != lcore_id || !local_is_locked)
|
|
|
|
rte_spinlock_unlock(&priv_timer[tim_lcore].list_lock);
|
|
|
|
|
2012-09-04 12:54:00 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Reset and start the timer associated with the timer handle tim */
|
|
|
|
int
|
2019-04-15 21:41:27 +00:00
|
|
|
rte_timer_reset_v20(struct rte_timer *tim, uint64_t ticks,
|
|
|
|
enum rte_timer_type type, unsigned int tim_lcore,
|
|
|
|
rte_timer_cb_t fct, void *arg)
|
2012-09-04 12:54:00 +00:00
|
|
|
{
|
2013-06-03 00:00:00 +00:00
|
|
|
uint64_t cur_time = rte_get_timer_cycles();
|
2012-09-04 12:54:00 +00:00
|
|
|
uint64_t period;
|
|
|
|
|
|
|
|
if (unlikely((tim_lcore != (unsigned)LCORE_ID_ANY) &&
|
2017-09-21 20:10:03 +00:00
|
|
|
!(rte_lcore_is_enabled(tim_lcore) ||
|
2018-04-26 13:42:31 +00:00
|
|
|
rte_lcore_has_role(tim_lcore, ROLE_SERVICE))))
|
2012-09-04 12:54:00 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (type == PERIODICAL)
|
|
|
|
period = ticks;
|
|
|
|
else
|
|
|
|
period = 0;
|
|
|
|
|
2015-02-25 04:09:49 +00:00
|
|
|
return __rte_timer_reset(tim, cur_time + ticks, period, tim_lcore,
|
2019-04-15 21:41:27 +00:00
|
|
|
fct, arg, 0, &default_timer_data);
|
|
|
|
}
|
|
|
|
VERSION_SYMBOL(rte_timer_reset, _v20, 2.0);
|
|
|
|
|
|
|
|
int
|
|
|
|
rte_timer_reset_v1905(struct rte_timer *tim, uint64_t ticks,
|
|
|
|
enum rte_timer_type type, unsigned int tim_lcore,
|
|
|
|
rte_timer_cb_t fct, void *arg)
|
|
|
|
{
|
|
|
|
return rte_timer_alt_reset(default_data_id, tim, ticks, type,
|
|
|
|
tim_lcore, fct, arg);
|
|
|
|
}
|
|
|
|
MAP_STATIC_SYMBOL(int rte_timer_reset(struct rte_timer *tim, uint64_t ticks,
|
|
|
|
enum rte_timer_type type,
|
|
|
|
unsigned int tim_lcore,
|
|
|
|
rte_timer_cb_t fct, void *arg),
|
|
|
|
rte_timer_reset_v1905);
|
|
|
|
BIND_DEFAULT_SYMBOL(rte_timer_reset, _v1905, 19.05);
|
|
|
|
|
2019-06-29 11:58:52 +00:00
|
|
|
int
|
2019-04-15 21:41:27 +00:00
|
|
|
rte_timer_alt_reset(uint32_t timer_data_id, struct rte_timer *tim,
|
|
|
|
uint64_t ticks, enum rte_timer_type type,
|
|
|
|
unsigned int tim_lcore, rte_timer_cb_t fct, void *arg)
|
|
|
|
{
|
|
|
|
uint64_t cur_time = rte_get_timer_cycles();
|
|
|
|
uint64_t period;
|
|
|
|
struct rte_timer_data *timer_data;
|
|
|
|
|
|
|
|
TIMER_DATA_VALID_GET_OR_ERR_RET(timer_data_id, timer_data, -EINVAL);
|
|
|
|
|
|
|
|
if (type == PERIODICAL)
|
|
|
|
period = ticks;
|
|
|
|
else
|
|
|
|
period = 0;
|
|
|
|
|
|
|
|
return __rte_timer_reset(tim, cur_time + ticks, period, tim_lcore,
|
|
|
|
fct, arg, 0, timer_data);
|
2012-09-04 12:54:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* loop until rte_timer_reset() succeed */
|
|
|
|
void
|
|
|
|
rte_timer_reset_sync(struct rte_timer *tim, uint64_t ticks,
|
|
|
|
enum rte_timer_type type, unsigned tim_lcore,
|
|
|
|
rte_timer_cb_t fct, void *arg)
|
|
|
|
{
|
|
|
|
while (rte_timer_reset(tim, ticks, type, tim_lcore,
|
2015-02-25 04:09:47 +00:00
|
|
|
fct, arg) != 0)
|
|
|
|
rte_pause();
|
2012-09-04 12:54:00 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 21:41:27 +00:00
|
|
|
static int
|
|
|
|
__rte_timer_stop(struct rte_timer *tim, int local_is_locked,
|
|
|
|
struct rte_timer_data *timer_data)
|
2012-09-04 12:54:00 +00:00
|
|
|
{
|
|
|
|
union rte_timer_status prev_status, status;
|
|
|
|
unsigned lcore_id = rte_lcore_id();
|
|
|
|
int ret;
|
2019-04-15 21:41:27 +00:00
|
|
|
struct priv_timer *priv_timer = timer_data->priv_timer;
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
/* wait that the timer is in correct status before update,
|
2013-09-18 10:00:00 +00:00
|
|
|
* and mark it as being configured */
|
2019-04-15 21:41:27 +00:00
|
|
|
ret = timer_set_config_state(tim, &prev_status, priv_timer);
|
2012-09-04 12:54:00 +00:00
|
|
|
if (ret < 0)
|
|
|
|
return -1;
|
|
|
|
|
2019-04-15 21:41:27 +00:00
|
|
|
__TIMER_STAT_ADD(priv_timer, stop, 1);
|
2015-02-17 02:08:16 +00:00
|
|
|
if (prev_status.state == RTE_TIMER_RUNNING &&
|
|
|
|
lcore_id < RTE_MAX_LCORE) {
|
2014-05-21 19:53:45 +00:00
|
|
|
priv_timer[lcore_id].updated = 1;
|
|
|
|
}
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
/* remove it from list */
|
2013-09-18 10:00:00 +00:00
|
|
|
if (prev_status.state == RTE_TIMER_PENDING) {
|
2019-04-15 21:41:27 +00:00
|
|
|
timer_del(tim, prev_status, local_is_locked, priv_timer);
|
|
|
|
__TIMER_STAT_ADD(priv_timer, pending, -1);
|
2012-09-04 12:54:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* mark timer as stopped */
|
|
|
|
rte_wmb();
|
|
|
|
status.state = RTE_TIMER_STOP;
|
|
|
|
status.owner = RTE_TIMER_NO_OWNER;
|
|
|
|
tim->status.u32 = status.u32;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-15 21:41:27 +00:00
|
|
|
/* Stop the timer associated with the timer handle tim */
|
|
|
|
int
|
|
|
|
rte_timer_stop_v20(struct rte_timer *tim)
|
|
|
|
{
|
|
|
|
return __rte_timer_stop(tim, 0, &default_timer_data);
|
|
|
|
}
|
|
|
|
VERSION_SYMBOL(rte_timer_stop, _v20, 2.0);
|
|
|
|
|
|
|
|
int
|
|
|
|
rte_timer_stop_v1905(struct rte_timer *tim)
|
|
|
|
{
|
|
|
|
return rte_timer_alt_stop(default_data_id, tim);
|
|
|
|
}
|
|
|
|
MAP_STATIC_SYMBOL(int rte_timer_stop(struct rte_timer *tim),
|
|
|
|
rte_timer_stop_v1905);
|
|
|
|
BIND_DEFAULT_SYMBOL(rte_timer_stop, _v1905, 19.05);
|
|
|
|
|
2019-06-29 11:58:52 +00:00
|
|
|
int
|
2019-04-15 21:41:27 +00:00
|
|
|
rte_timer_alt_stop(uint32_t timer_data_id, struct rte_timer *tim)
|
|
|
|
{
|
|
|
|
struct rte_timer_data *timer_data;
|
|
|
|
|
|
|
|
TIMER_DATA_VALID_GET_OR_ERR_RET(timer_data_id, timer_data, -EINVAL);
|
|
|
|
|
|
|
|
return __rte_timer_stop(tim, 0, timer_data);
|
|
|
|
}
|
|
|
|
|
2012-09-04 12:54:00 +00:00
|
|
|
/* loop until rte_timer_stop() succeed */
|
|
|
|
void
|
|
|
|
rte_timer_stop_sync(struct rte_timer *tim)
|
|
|
|
{
|
2013-06-03 00:00:00 +00:00
|
|
|
while (rte_timer_stop(tim) != 0)
|
|
|
|
rte_pause();
|
2012-09-04 12:54:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Test the PENDING status of the timer handle tim */
|
|
|
|
int
|
|
|
|
rte_timer_pending(struct rte_timer *tim)
|
|
|
|
{
|
|
|
|
return tim->status.state == RTE_TIMER_PENDING;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* must be called periodically, run all timer that expired */
|
2019-04-15 21:41:27 +00:00
|
|
|
static void
|
|
|
|
__rte_timer_manage(struct rte_timer_data *timer_data)
|
2012-09-04 12:54:00 +00:00
|
|
|
{
|
|
|
|
union rte_timer_status status;
|
2013-09-18 10:00:00 +00:00
|
|
|
struct rte_timer *tim, *next_tim;
|
timer: fix race condition
Eliminate problematic race condition in rte_timer_manage() that can
lead to corruption of per-lcore pending-lists (implemented as
skip-lists). The race condition occurs when rte_timer_manage() expires
multiple timers on lcore A, while lcore B simultaneously invokes
rte_timer_reset() for one of the expiring timers (other than the first
one).
Lcore A splits its pending-list, creating a local list of expired timers
linked through their sl_next[0] pointers, and sets the first expired
timer to the RUNNING state, all during one list-lock round trip.
Lcore A then unlocks the list-lock to run the first callback, and that
is when A and B can have different interpretations of the subsequent
expired timers' true state. Lcore B sees an expired timer still in the
PENDING state, atomically changes the timer to the CONFIG state, locks
lcore A's list-lock, and reinserts the timer into A's pending-list.
The two lcores try to use the same next-pointers to maintain both lists!
Our solution is to remove expired timers from the pending-list and try
to set them all to the RUNNING state in one atomic step, i.e.,
rte_timer_manage() should perform these two actions within one
ownership of the list-lock.
After splitting the pending-list at the current point in time and trying
to set all expired timers to the RUNNING state, we must put back into
the pending-list any timers that we failed to set to the RUNNING state,
all while still holding the list-lock. It is then safe to release the
lock and run the callback functions for all expired timers that remain
on our local run-list.
Signed-off-by: Robert Sanford <rsanford@akamai.com>
2015-07-27 22:46:06 +00:00
|
|
|
struct rte_timer *run_first_tim, **pprev;
|
2012-09-04 12:54:00 +00:00
|
|
|
unsigned lcore_id = rte_lcore_id();
|
2013-09-18 10:00:00 +00:00
|
|
|
struct rte_timer *prev[MAX_SKIPLIST_DEPTH + 1];
|
2013-05-30 17:12:36 +00:00
|
|
|
uint64_t cur_time;
|
2013-09-18 10:00:00 +00:00
|
|
|
int i, ret;
|
2019-04-15 21:41:27 +00:00
|
|
|
struct priv_timer *priv_timer = timer_data->priv_timer;
|
2012-09-04 12:54:00 +00:00
|
|
|
|
2015-02-17 02:08:16 +00:00
|
|
|
/* timer manager only runs on EAL thread with valid lcore_id */
|
|
|
|
assert(lcore_id < RTE_MAX_LCORE);
|
|
|
|
|
2019-04-15 21:41:27 +00:00
|
|
|
__TIMER_STAT_ADD(priv_timer, manage, 1);
|
2013-05-30 17:12:36 +00:00
|
|
|
/* optimize for the case where per-cpu list is empty */
|
2013-09-18 10:00:00 +00:00
|
|
|
if (priv_timer[lcore_id].pending_head.sl_next[0] == NULL)
|
2013-05-30 17:12:36 +00:00
|
|
|
return;
|
2013-06-03 00:00:00 +00:00
|
|
|
cur_time = rte_get_timer_cycles();
|
2012-09-04 12:54:00 +00:00
|
|
|
|
2017-08-13 12:33:38 +00:00
|
|
|
#ifdef RTE_ARCH_64
|
timer: fix race condition
Eliminate problematic race condition in rte_timer_manage() that can
lead to corruption of per-lcore pending-lists (implemented as
skip-lists). The race condition occurs when rte_timer_manage() expires
multiple timers on lcore A, while lcore B simultaneously invokes
rte_timer_reset() for one of the expiring timers (other than the first
one).
Lcore A splits its pending-list, creating a local list of expired timers
linked through their sl_next[0] pointers, and sets the first expired
timer to the RUNNING state, all during one list-lock round trip.
Lcore A then unlocks the list-lock to run the first callback, and that
is when A and B can have different interpretations of the subsequent
expired timers' true state. Lcore B sees an expired timer still in the
PENDING state, atomically changes the timer to the CONFIG state, locks
lcore A's list-lock, and reinserts the timer into A's pending-list.
The two lcores try to use the same next-pointers to maintain both lists!
Our solution is to remove expired timers from the pending-list and try
to set them all to the RUNNING state in one atomic step, i.e.,
rte_timer_manage() should perform these two actions within one
ownership of the list-lock.
After splitting the pending-list at the current point in time and trying
to set all expired timers to the RUNNING state, we must put back into
the pending-list any timers that we failed to set to the RUNNING state,
all while still holding the list-lock. It is then safe to release the
lock and run the callback functions for all expired timers that remain
on our local run-list.
Signed-off-by: Robert Sanford <rsanford@akamai.com>
2015-07-27 22:46:06 +00:00
|
|
|
/* on 64-bit the value cached in the pending_head.expired will be
|
|
|
|
* updated atomically, so we can consult that for a quick check here
|
|
|
|
* outside the lock */
|
2013-09-18 10:00:00 +00:00
|
|
|
if (likely(priv_timer[lcore_id].pending_head.expire > cur_time))
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
2012-09-04 12:54:00 +00:00
|
|
|
/* browse ordered list, add expired timers in 'expired' list */
|
|
|
|
rte_spinlock_lock(&priv_timer[lcore_id].list_lock);
|
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
/* if nothing to do just unlock and return */
|
|
|
|
if (priv_timer[lcore_id].pending_head.sl_next[0] == NULL ||
|
timer: fix race condition
Eliminate problematic race condition in rte_timer_manage() that can
lead to corruption of per-lcore pending-lists (implemented as
skip-lists). The race condition occurs when rte_timer_manage() expires
multiple timers on lcore A, while lcore B simultaneously invokes
rte_timer_reset() for one of the expiring timers (other than the first
one).
Lcore A splits its pending-list, creating a local list of expired timers
linked through their sl_next[0] pointers, and sets the first expired
timer to the RUNNING state, all during one list-lock round trip.
Lcore A then unlocks the list-lock to run the first callback, and that
is when A and B can have different interpretations of the subsequent
expired timers' true state. Lcore B sees an expired timer still in the
PENDING state, atomically changes the timer to the CONFIG state, locks
lcore A's list-lock, and reinserts the timer into A's pending-list.
The two lcores try to use the same next-pointers to maintain both lists!
Our solution is to remove expired timers from the pending-list and try
to set them all to the RUNNING state in one atomic step, i.e.,
rte_timer_manage() should perform these two actions within one
ownership of the list-lock.
After splitting the pending-list at the current point in time and trying
to set all expired timers to the RUNNING state, we must put back into
the pending-list any timers that we failed to set to the RUNNING state,
all while still holding the list-lock. It is then safe to release the
lock and run the callback functions for all expired timers that remain
on our local run-list.
Signed-off-by: Robert Sanford <rsanford@akamai.com>
2015-07-27 22:46:06 +00:00
|
|
|
priv_timer[lcore_id].pending_head.sl_next[0]->expire > cur_time) {
|
|
|
|
rte_spinlock_unlock(&priv_timer[lcore_id].list_lock);
|
|
|
|
return;
|
|
|
|
}
|
2013-09-18 10:00:00 +00:00
|
|
|
|
|
|
|
/* save start of list of expired timers */
|
|
|
|
tim = priv_timer[lcore_id].pending_head.sl_next[0];
|
|
|
|
|
|
|
|
/* break the existing list at current time point */
|
2019-04-15 21:41:27 +00:00
|
|
|
timer_get_prev_entries(cur_time, lcore_id, prev, priv_timer);
|
2013-09-18 10:00:00 +00:00
|
|
|
for (i = priv_timer[lcore_id].curr_skiplist_depth -1; i >= 0; i--) {
|
2016-07-17 14:35:39 +00:00
|
|
|
if (prev[i] == &priv_timer[lcore_id].pending_head)
|
|
|
|
continue;
|
timer: fix race condition
Eliminate problematic race condition in rte_timer_manage() that can
lead to corruption of per-lcore pending-lists (implemented as
skip-lists). The race condition occurs when rte_timer_manage() expires
multiple timers on lcore A, while lcore B simultaneously invokes
rte_timer_reset() for one of the expiring timers (other than the first
one).
Lcore A splits its pending-list, creating a local list of expired timers
linked through their sl_next[0] pointers, and sets the first expired
timer to the RUNNING state, all during one list-lock round trip.
Lcore A then unlocks the list-lock to run the first callback, and that
is when A and B can have different interpretations of the subsequent
expired timers' true state. Lcore B sees an expired timer still in the
PENDING state, atomically changes the timer to the CONFIG state, locks
lcore A's list-lock, and reinserts the timer into A's pending-list.
The two lcores try to use the same next-pointers to maintain both lists!
Our solution is to remove expired timers from the pending-list and try
to set them all to the RUNNING state in one atomic step, i.e.,
rte_timer_manage() should perform these two actions within one
ownership of the list-lock.
After splitting the pending-list at the current point in time and trying
to set all expired timers to the RUNNING state, we must put back into
the pending-list any timers that we failed to set to the RUNNING state,
all while still holding the list-lock. It is then safe to release the
lock and run the callback functions for all expired timers that remain
on our local run-list.
Signed-off-by: Robert Sanford <rsanford@akamai.com>
2015-07-27 22:46:06 +00:00
|
|
|
priv_timer[lcore_id].pending_head.sl_next[i] =
|
|
|
|
prev[i]->sl_next[i];
|
2013-09-18 10:00:00 +00:00
|
|
|
if (prev[i]->sl_next[i] == NULL)
|
|
|
|
priv_timer[lcore_id].curr_skiplist_depth--;
|
|
|
|
prev[i] ->sl_next[i] = NULL;
|
2012-09-04 12:54:00 +00:00
|
|
|
}
|
|
|
|
|
timer: fix race condition
Eliminate problematic race condition in rte_timer_manage() that can
lead to corruption of per-lcore pending-lists (implemented as
skip-lists). The race condition occurs when rte_timer_manage() expires
multiple timers on lcore A, while lcore B simultaneously invokes
rte_timer_reset() for one of the expiring timers (other than the first
one).
Lcore A splits its pending-list, creating a local list of expired timers
linked through their sl_next[0] pointers, and sets the first expired
timer to the RUNNING state, all during one list-lock round trip.
Lcore A then unlocks the list-lock to run the first callback, and that
is when A and B can have different interpretations of the subsequent
expired timers' true state. Lcore B sees an expired timer still in the
PENDING state, atomically changes the timer to the CONFIG state, locks
lcore A's list-lock, and reinserts the timer into A's pending-list.
The two lcores try to use the same next-pointers to maintain both lists!
Our solution is to remove expired timers from the pending-list and try
to set them all to the RUNNING state in one atomic step, i.e.,
rte_timer_manage() should perform these two actions within one
ownership of the list-lock.
After splitting the pending-list at the current point in time and trying
to set all expired timers to the RUNNING state, we must put back into
the pending-list any timers that we failed to set to the RUNNING state,
all while still holding the list-lock. It is then safe to release the
lock and run the callback functions for all expired timers that remain
on our local run-list.
Signed-off-by: Robert Sanford <rsanford@akamai.com>
2015-07-27 22:46:06 +00:00
|
|
|
/* transition run-list from PENDING to RUNNING */
|
|
|
|
run_first_tim = tim;
|
|
|
|
pprev = &run_first_tim;
|
|
|
|
|
2013-09-18 10:00:00 +00:00
|
|
|
for ( ; tim != NULL; tim = next_tim) {
|
|
|
|
next_tim = tim->sl_next[0];
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
ret = timer_set_running_state(tim);
|
timer: fix race condition
Eliminate problematic race condition in rte_timer_manage() that can
lead to corruption of per-lcore pending-lists (implemented as
skip-lists). The race condition occurs when rte_timer_manage() expires
multiple timers on lcore A, while lcore B simultaneously invokes
rte_timer_reset() for one of the expiring timers (other than the first
one).
Lcore A splits its pending-list, creating a local list of expired timers
linked through their sl_next[0] pointers, and sets the first expired
timer to the RUNNING state, all during one list-lock round trip.
Lcore A then unlocks the list-lock to run the first callback, and that
is when A and B can have different interpretations of the subsequent
expired timers' true state. Lcore B sees an expired timer still in the
PENDING state, atomically changes the timer to the CONFIG state, locks
lcore A's list-lock, and reinserts the timer into A's pending-list.
The two lcores try to use the same next-pointers to maintain both lists!
Our solution is to remove expired timers from the pending-list and try
to set them all to the RUNNING state in one atomic step, i.e.,
rte_timer_manage() should perform these two actions within one
ownership of the list-lock.
After splitting the pending-list at the current point in time and trying
to set all expired timers to the RUNNING state, we must put back into
the pending-list any timers that we failed to set to the RUNNING state,
all while still holding the list-lock. It is then safe to release the
lock and run the callback functions for all expired timers that remain
on our local run-list.
Signed-off-by: Robert Sanford <rsanford@akamai.com>
2015-07-27 22:46:06 +00:00
|
|
|
if (likely(ret == 0)) {
|
|
|
|
pprev = &tim->sl_next[0];
|
|
|
|
} else {
|
|
|
|
/* another core is trying to re-config this one,
|
2016-07-17 17:35:50 +00:00
|
|
|
* remove it from local expired list
|
|
|
|
*/
|
timer: fix race condition
Eliminate problematic race condition in rte_timer_manage() that can
lead to corruption of per-lcore pending-lists (implemented as
skip-lists). The race condition occurs when rte_timer_manage() expires
multiple timers on lcore A, while lcore B simultaneously invokes
rte_timer_reset() for one of the expiring timers (other than the first
one).
Lcore A splits its pending-list, creating a local list of expired timers
linked through their sl_next[0] pointers, and sets the first expired
timer to the RUNNING state, all during one list-lock round trip.
Lcore A then unlocks the list-lock to run the first callback, and that
is when A and B can have different interpretations of the subsequent
expired timers' true state. Lcore B sees an expired timer still in the
PENDING state, atomically changes the timer to the CONFIG state, locks
lcore A's list-lock, and reinserts the timer into A's pending-list.
The two lcores try to use the same next-pointers to maintain both lists!
Our solution is to remove expired timers from the pending-list and try
to set them all to the RUNNING state in one atomic step, i.e.,
rte_timer_manage() should perform these two actions within one
ownership of the list-lock.
After splitting the pending-list at the current point in time and trying
to set all expired timers to the RUNNING state, we must put back into
the pending-list any timers that we failed to set to the RUNNING state,
all while still holding the list-lock. It is then safe to release the
lock and run the callback functions for all expired timers that remain
on our local run-list.
Signed-off-by: Robert Sanford <rsanford@akamai.com>
2015-07-27 22:46:06 +00:00
|
|
|
*pprev = next_tim;
|
|
|
|
}
|
|
|
|
}
|
2012-09-04 12:54:00 +00:00
|
|
|
|
timer: fix race condition
Eliminate problematic race condition in rte_timer_manage() that can
lead to corruption of per-lcore pending-lists (implemented as
skip-lists). The race condition occurs when rte_timer_manage() expires
multiple timers on lcore A, while lcore B simultaneously invokes
rte_timer_reset() for one of the expiring timers (other than the first
one).
Lcore A splits its pending-list, creating a local list of expired timers
linked through their sl_next[0] pointers, and sets the first expired
timer to the RUNNING state, all during one list-lock round trip.
Lcore A then unlocks the list-lock to run the first callback, and that
is when A and B can have different interpretations of the subsequent
expired timers' true state. Lcore B sees an expired timer still in the
PENDING state, atomically changes the timer to the CONFIG state, locks
lcore A's list-lock, and reinserts the timer into A's pending-list.
The two lcores try to use the same next-pointers to maintain both lists!
Our solution is to remove expired timers from the pending-list and try
to set them all to the RUNNING state in one atomic step, i.e.,
rte_timer_manage() should perform these two actions within one
ownership of the list-lock.
After splitting the pending-list at the current point in time and trying
to set all expired timers to the RUNNING state, we must put back into
the pending-list any timers that we failed to set to the RUNNING state,
all while still holding the list-lock. It is then safe to release the
lock and run the callback functions for all expired timers that remain
on our local run-list.
Signed-off-by: Robert Sanford <rsanford@akamai.com>
2015-07-27 22:46:06 +00:00
|
|
|
/* update the next to expire timer value */
|
|
|
|
priv_timer[lcore_id].pending_head.expire =
|
|
|
|
(priv_timer[lcore_id].pending_head.sl_next[0] == NULL) ? 0 :
|
|
|
|
priv_timer[lcore_id].pending_head.sl_next[0]->expire;
|
2012-09-04 12:54:00 +00:00
|
|
|
|
timer: fix race condition
Eliminate problematic race condition in rte_timer_manage() that can
lead to corruption of per-lcore pending-lists (implemented as
skip-lists). The race condition occurs when rte_timer_manage() expires
multiple timers on lcore A, while lcore B simultaneously invokes
rte_timer_reset() for one of the expiring timers (other than the first
one).
Lcore A splits its pending-list, creating a local list of expired timers
linked through their sl_next[0] pointers, and sets the first expired
timer to the RUNNING state, all during one list-lock round trip.
Lcore A then unlocks the list-lock to run the first callback, and that
is when A and B can have different interpretations of the subsequent
expired timers' true state. Lcore B sees an expired timer still in the
PENDING state, atomically changes the timer to the CONFIG state, locks
lcore A's list-lock, and reinserts the timer into A's pending-list.
The two lcores try to use the same next-pointers to maintain both lists!
Our solution is to remove expired timers from the pending-list and try
to set them all to the RUNNING state in one atomic step, i.e.,
rte_timer_manage() should perform these two actions within one
ownership of the list-lock.
After splitting the pending-list at the current point in time and trying
to set all expired timers to the RUNNING state, we must put back into
the pending-list any timers that we failed to set to the RUNNING state,
all while still holding the list-lock. It is then safe to release the
lock and run the callback functions for all expired timers that remain
on our local run-list.
Signed-off-by: Robert Sanford <rsanford@akamai.com>
2015-07-27 22:46:06 +00:00
|
|
|
rte_spinlock_unlock(&priv_timer[lcore_id].list_lock);
|
2012-09-04 12:54:00 +00:00
|
|
|
|
timer: fix race condition
Eliminate problematic race condition in rte_timer_manage() that can
lead to corruption of per-lcore pending-lists (implemented as
skip-lists). The race condition occurs when rte_timer_manage() expires
multiple timers on lcore A, while lcore B simultaneously invokes
rte_timer_reset() for one of the expiring timers (other than the first
one).
Lcore A splits its pending-list, creating a local list of expired timers
linked through their sl_next[0] pointers, and sets the first expired
timer to the RUNNING state, all during one list-lock round trip.
Lcore A then unlocks the list-lock to run the first callback, and that
is when A and B can have different interpretations of the subsequent
expired timers' true state. Lcore B sees an expired timer still in the
PENDING state, atomically changes the timer to the CONFIG state, locks
lcore A's list-lock, and reinserts the timer into A's pending-list.
The two lcores try to use the same next-pointers to maintain both lists!
Our solution is to remove expired timers from the pending-list and try
to set them all to the RUNNING state in one atomic step, i.e.,
rte_timer_manage() should perform these two actions within one
ownership of the list-lock.
After splitting the pending-list at the current point in time and trying
to set all expired timers to the RUNNING state, we must put back into
the pending-list any timers that we failed to set to the RUNNING state,
all while still holding the list-lock. It is then safe to release the
lock and run the callback functions for all expired timers that remain
on our local run-list.
Signed-off-by: Robert Sanford <rsanford@akamai.com>
2015-07-27 22:46:06 +00:00
|
|
|
/* now scan expired list and call callbacks */
|
|
|
|
for (tim = run_first_tim; tim != NULL; tim = next_tim) {
|
|
|
|
next_tim = tim->sl_next[0];
|
2012-09-04 12:54:00 +00:00
|
|
|
priv_timer[lcore_id].updated = 0;
|
2016-07-17 18:08:00 +00:00
|
|
|
priv_timer[lcore_id].running_tim = tim;
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
/* execute callback function with list unlocked */
|
|
|
|
tim->f(tim, tim->arg);
|
|
|
|
|
2019-04-15 21:41:27 +00:00
|
|
|
__TIMER_STAT_ADD(priv_timer, pending, -1);
|
2012-09-04 12:54:00 +00:00
|
|
|
/* the timer was stopped or reloaded by the callback
|
|
|
|
* function, we have nothing to do here */
|
|
|
|
if (priv_timer[lcore_id].updated == 1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (tim->period == 0) {
|
|
|
|
/* remove from done list and mark timer as stopped */
|
|
|
|
status.state = RTE_TIMER_STOP;
|
|
|
|
status.owner = RTE_TIMER_NO_OWNER;
|
|
|
|
rte_wmb();
|
|
|
|
tim->status.u32 = status.u32;
|
|
|
|
}
|
|
|
|
else {
|
2013-09-18 10:00:00 +00:00
|
|
|
/* keep it in list and mark timer as pending */
|
timer: fix race condition
Eliminate problematic race condition in rte_timer_manage() that can
lead to corruption of per-lcore pending-lists (implemented as
skip-lists). The race condition occurs when rte_timer_manage() expires
multiple timers on lcore A, while lcore B simultaneously invokes
rte_timer_reset() for one of the expiring timers (other than the first
one).
Lcore A splits its pending-list, creating a local list of expired timers
linked through their sl_next[0] pointers, and sets the first expired
timer to the RUNNING state, all during one list-lock round trip.
Lcore A then unlocks the list-lock to run the first callback, and that
is when A and B can have different interpretations of the subsequent
expired timers' true state. Lcore B sees an expired timer still in the
PENDING state, atomically changes the timer to the CONFIG state, locks
lcore A's list-lock, and reinserts the timer into A's pending-list.
The two lcores try to use the same next-pointers to maintain both lists!
Our solution is to remove expired timers from the pending-list and try
to set them all to the RUNNING state in one atomic step, i.e.,
rte_timer_manage() should perform these two actions within one
ownership of the list-lock.
After splitting the pending-list at the current point in time and trying
to set all expired timers to the RUNNING state, we must put back into
the pending-list any timers that we failed to set to the RUNNING state,
all while still holding the list-lock. It is then safe to release the
lock and run the callback functions for all expired timers that remain
on our local run-list.
Signed-off-by: Robert Sanford <rsanford@akamai.com>
2015-07-27 22:46:06 +00:00
|
|
|
rte_spinlock_lock(&priv_timer[lcore_id].list_lock);
|
2012-09-04 12:54:00 +00:00
|
|
|
status.state = RTE_TIMER_PENDING;
|
2019-04-15 21:41:27 +00:00
|
|
|
__TIMER_STAT_ADD(priv_timer, pending, 1);
|
2012-09-04 12:54:00 +00:00
|
|
|
status.owner = (int16_t)lcore_id;
|
|
|
|
rte_wmb();
|
|
|
|
tim->status.u32 = status.u32;
|
2016-09-21 20:54:27 +00:00
|
|
|
__rte_timer_reset(tim, tim->expire + tim->period,
|
2019-04-15 21:41:27 +00:00
|
|
|
tim->period, lcore_id, tim->f, tim->arg, 1,
|
|
|
|
timer_data);
|
timer: fix race condition
Eliminate problematic race condition in rte_timer_manage() that can
lead to corruption of per-lcore pending-lists (implemented as
skip-lists). The race condition occurs when rte_timer_manage() expires
multiple timers on lcore A, while lcore B simultaneously invokes
rte_timer_reset() for one of the expiring timers (other than the first
one).
Lcore A splits its pending-list, creating a local list of expired timers
linked through their sl_next[0] pointers, and sets the first expired
timer to the RUNNING state, all during one list-lock round trip.
Lcore A then unlocks the list-lock to run the first callback, and that
is when A and B can have different interpretations of the subsequent
expired timers' true state. Lcore B sees an expired timer still in the
PENDING state, atomically changes the timer to the CONFIG state, locks
lcore A's list-lock, and reinserts the timer into A's pending-list.
The two lcores try to use the same next-pointers to maintain both lists!
Our solution is to remove expired timers from the pending-list and try
to set them all to the RUNNING state in one atomic step, i.e.,
rte_timer_manage() should perform these two actions within one
ownership of the list-lock.
After splitting the pending-list at the current point in time and trying
to set all expired timers to the RUNNING state, we must put back into
the pending-list any timers that we failed to set to the RUNNING state,
all while still holding the list-lock. It is then safe to release the
lock and run the callback functions for all expired timers that remain
on our local run-list.
Signed-off-by: Robert Sanford <rsanford@akamai.com>
2015-07-27 22:46:06 +00:00
|
|
|
rte_spinlock_unlock(&priv_timer[lcore_id].list_lock);
|
2012-09-04 12:54:00 +00:00
|
|
|
}
|
|
|
|
}
|
2016-07-17 18:08:00 +00:00
|
|
|
priv_timer[lcore_id].running_tim = NULL;
|
2012-09-04 12:54:00 +00:00
|
|
|
}
|
|
|
|
|
2019-04-15 21:41:27 +00:00
|
|
|
void
|
|
|
|
rte_timer_manage_v20(void)
|
|
|
|
{
|
|
|
|
__rte_timer_manage(&default_timer_data);
|
|
|
|
}
|
|
|
|
VERSION_SYMBOL(rte_timer_manage, _v20, 2.0);
|
|
|
|
|
|
|
|
int
|
|
|
|
rte_timer_manage_v1905(void)
|
|
|
|
{
|
|
|
|
struct rte_timer_data *timer_data;
|
|
|
|
|
|
|
|
TIMER_DATA_VALID_GET_OR_ERR_RET(default_data_id, timer_data, -EINVAL);
|
|
|
|
|
|
|
|
__rte_timer_manage(timer_data);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
MAP_STATIC_SYMBOL(int rte_timer_manage(void), rte_timer_manage_v1905);
|
|
|
|
BIND_DEFAULT_SYMBOL(rte_timer_manage, _v1905, 19.05);
|
|
|
|
|
2019-06-29 11:58:52 +00:00
|
|
|
int
|
2019-04-15 21:41:27 +00:00
|
|
|
rte_timer_alt_manage(uint32_t timer_data_id,
|
|
|
|
unsigned int *poll_lcores,
|
|
|
|
int nb_poll_lcores,
|
|
|
|
rte_timer_alt_manage_cb_t f)
|
|
|
|
{
|
2019-04-24 14:33:50 +00:00
|
|
|
unsigned int default_poll_lcores[] = {rte_lcore_id()};
|
2019-04-15 21:41:27 +00:00
|
|
|
union rte_timer_status status;
|
|
|
|
struct rte_timer *tim, *next_tim, **pprev;
|
|
|
|
struct rte_timer *run_first_tims[RTE_MAX_LCORE];
|
|
|
|
unsigned int this_lcore = rte_lcore_id();
|
|
|
|
struct rte_timer *prev[MAX_SKIPLIST_DEPTH + 1];
|
|
|
|
uint64_t cur_time;
|
|
|
|
int i, j, ret;
|
|
|
|
int nb_runlists = 0;
|
|
|
|
struct rte_timer_data *data;
|
|
|
|
struct priv_timer *privp;
|
|
|
|
uint32_t poll_lcore;
|
|
|
|
|
|
|
|
TIMER_DATA_VALID_GET_OR_ERR_RET(timer_data_id, data, -EINVAL);
|
|
|
|
|
|
|
|
/* timer manager only runs on EAL thread with valid lcore_id */
|
|
|
|
assert(this_lcore < RTE_MAX_LCORE);
|
|
|
|
|
|
|
|
__TIMER_STAT_ADD(data->priv_timer, manage, 1);
|
|
|
|
|
|
|
|
if (poll_lcores == NULL) {
|
2019-04-24 14:33:50 +00:00
|
|
|
poll_lcores = default_poll_lcores;
|
|
|
|
nb_poll_lcores = RTE_DIM(default_poll_lcores);
|
2019-04-15 21:41:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < nb_poll_lcores; i++) {
|
|
|
|
poll_lcore = poll_lcores[i];
|
|
|
|
privp = &data->priv_timer[poll_lcore];
|
|
|
|
|
|
|
|
/* optimize for the case where per-cpu list is empty */
|
|
|
|
if (privp->pending_head.sl_next[0] == NULL)
|
|
|
|
continue;
|
|
|
|
cur_time = rte_get_timer_cycles();
|
|
|
|
|
|
|
|
#ifdef RTE_ARCH_64
|
|
|
|
/* on 64-bit the value cached in the pending_head.expired will
|
|
|
|
* be updated atomically, so we can consult that for a quick
|
|
|
|
* check here outside the lock
|
|
|
|
*/
|
|
|
|
if (likely(privp->pending_head.expire > cur_time))
|
|
|
|
continue;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* browse ordered list, add expired timers in 'expired' list */
|
|
|
|
rte_spinlock_lock(&privp->list_lock);
|
|
|
|
|
|
|
|
/* if nothing to do just unlock and return */
|
|
|
|
if (privp->pending_head.sl_next[0] == NULL ||
|
|
|
|
privp->pending_head.sl_next[0]->expire > cur_time) {
|
|
|
|
rte_spinlock_unlock(&privp->list_lock);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* save start of list of expired timers */
|
|
|
|
tim = privp->pending_head.sl_next[0];
|
|
|
|
|
|
|
|
/* break the existing list at current time point */
|
|
|
|
timer_get_prev_entries(cur_time, poll_lcore, prev,
|
|
|
|
data->priv_timer);
|
|
|
|
for (j = privp->curr_skiplist_depth - 1; j >= 0; j--) {
|
|
|
|
if (prev[j] == &privp->pending_head)
|
|
|
|
continue;
|
|
|
|
privp->pending_head.sl_next[j] =
|
|
|
|
prev[j]->sl_next[j];
|
|
|
|
if (prev[j]->sl_next[j] == NULL)
|
|
|
|
privp->curr_skiplist_depth--;
|
|
|
|
|
|
|
|
prev[j]->sl_next[j] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* transition run-list from PENDING to RUNNING */
|
|
|
|
run_first_tims[nb_runlists] = tim;
|
|
|
|
pprev = &run_first_tims[nb_runlists];
|
|
|
|
nb_runlists++;
|
|
|
|
|
|
|
|
for ( ; tim != NULL; tim = next_tim) {
|
|
|
|
next_tim = tim->sl_next[0];
|
|
|
|
|
|
|
|
ret = timer_set_running_state(tim);
|
|
|
|
if (likely(ret == 0)) {
|
|
|
|
pprev = &tim->sl_next[0];
|
|
|
|
} else {
|
|
|
|
/* another core is trying to re-config this one,
|
|
|
|
* remove it from local expired list
|
|
|
|
*/
|
|
|
|
*pprev = next_tim;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update the next to expire timer value */
|
|
|
|
privp->pending_head.expire =
|
|
|
|
(privp->pending_head.sl_next[0] == NULL) ? 0 :
|
|
|
|
privp->pending_head.sl_next[0]->expire;
|
|
|
|
|
|
|
|
rte_spinlock_unlock(&privp->list_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now process the run lists */
|
|
|
|
while (1) {
|
|
|
|
bool done = true;
|
|
|
|
uint64_t min_expire = UINT64_MAX;
|
|
|
|
int min_idx = 0;
|
|
|
|
|
|
|
|
/* Find the next oldest timer to process */
|
|
|
|
for (i = 0; i < nb_runlists; i++) {
|
|
|
|
tim = run_first_tims[i];
|
|
|
|
|
|
|
|
if (tim != NULL && tim->expire < min_expire) {
|
|
|
|
min_expire = tim->expire;
|
|
|
|
min_idx = i;
|
|
|
|
done = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (done)
|
|
|
|
break;
|
|
|
|
|
|
|
|
tim = run_first_tims[min_idx];
|
|
|
|
|
|
|
|
/* Move down the runlist from which we picked a timer to
|
|
|
|
* execute
|
|
|
|
*/
|
|
|
|
run_first_tims[min_idx] = run_first_tims[min_idx]->sl_next[0];
|
|
|
|
|
2019-04-26 14:41:24 +00:00
|
|
|
data->priv_timer[this_lcore].updated = 0;
|
|
|
|
data->priv_timer[this_lcore].running_tim = tim;
|
2019-04-15 21:41:27 +00:00
|
|
|
|
|
|
|
/* Call the provided callback function */
|
|
|
|
f(tim);
|
|
|
|
|
2019-04-26 14:41:24 +00:00
|
|
|
__TIMER_STAT_ADD(data->priv_timer, pending, -1);
|
2019-04-15 21:41:27 +00:00
|
|
|
|
|
|
|
/* the timer was stopped or reloaded by the callback
|
|
|
|
* function, we have nothing to do here
|
|
|
|
*/
|
2019-04-26 14:41:24 +00:00
|
|
|
if (data->priv_timer[this_lcore].updated == 1)
|
2019-04-15 21:41:27 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (tim->period == 0) {
|
|
|
|
/* remove from done list and mark timer as stopped */
|
|
|
|
status.state = RTE_TIMER_STOP;
|
|
|
|
status.owner = RTE_TIMER_NO_OWNER;
|
|
|
|
rte_wmb();
|
|
|
|
tim->status.u32 = status.u32;
|
|
|
|
} else {
|
|
|
|
/* keep it in list and mark timer as pending */
|
|
|
|
rte_spinlock_lock(
|
|
|
|
&data->priv_timer[this_lcore].list_lock);
|
|
|
|
status.state = RTE_TIMER_PENDING;
|
|
|
|
__TIMER_STAT_ADD(data->priv_timer, pending, 1);
|
|
|
|
status.owner = (int16_t)this_lcore;
|
|
|
|
rte_wmb();
|
|
|
|
tim->status.u32 = status.u32;
|
|
|
|
__rte_timer_reset(tim, tim->expire + tim->period,
|
|
|
|
tim->period, this_lcore, tim->f, tim->arg, 1,
|
|
|
|
data);
|
|
|
|
rte_spinlock_unlock(
|
|
|
|
&data->priv_timer[this_lcore].list_lock);
|
|
|
|
}
|
|
|
|
|
2019-04-26 14:41:24 +00:00
|
|
|
data->priv_timer[this_lcore].running_tim = NULL;
|
2019-04-15 21:41:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-15 21:41:28 +00:00
|
|
|
/* Walk pending lists, stopping timers and calling user-specified function */
|
2019-06-29 11:58:52 +00:00
|
|
|
int
|
2019-04-15 21:41:28 +00:00
|
|
|
rte_timer_stop_all(uint32_t timer_data_id, unsigned int *walk_lcores,
|
|
|
|
int nb_walk_lcores,
|
|
|
|
rte_timer_stop_all_cb_t f, void *f_arg)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct priv_timer *priv_timer;
|
|
|
|
uint32_t walk_lcore;
|
|
|
|
struct rte_timer *tim, *next_tim;
|
|
|
|
struct rte_timer_data *timer_data;
|
|
|
|
|
|
|
|
TIMER_DATA_VALID_GET_OR_ERR_RET(timer_data_id, timer_data, -EINVAL);
|
|
|
|
|
|
|
|
for (i = 0; i < nb_walk_lcores; i++) {
|
|
|
|
walk_lcore = walk_lcores[i];
|
|
|
|
priv_timer = &timer_data->priv_timer[walk_lcore];
|
|
|
|
|
|
|
|
rte_spinlock_lock(&priv_timer->list_lock);
|
|
|
|
|
|
|
|
for (tim = priv_timer->pending_head.sl_next[0];
|
|
|
|
tim != NULL;
|
|
|
|
tim = next_tim) {
|
|
|
|
next_tim = tim->sl_next[0];
|
|
|
|
|
|
|
|
/* Call timer_stop with lock held */
|
|
|
|
__rte_timer_stop(tim, 1, timer_data);
|
|
|
|
|
|
|
|
if (f)
|
|
|
|
f(tim, f_arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
rte_spinlock_unlock(&priv_timer->list_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-09-04 12:54:00 +00:00
|
|
|
/* dump statistics about timers */
|
2019-04-15 21:41:27 +00:00
|
|
|
static void
|
|
|
|
__rte_timer_dump_stats(struct rte_timer_data *timer_data __rte_unused, FILE *f)
|
2012-09-04 12:54:00 +00:00
|
|
|
{
|
|
|
|
#ifdef RTE_LIBRTE_TIMER_DEBUG
|
|
|
|
struct rte_timer_debug_stats sum;
|
|
|
|
unsigned lcore_id;
|
2019-04-15 21:41:27 +00:00
|
|
|
struct priv_timer *priv_timer = timer_data->priv_timer;
|
2012-09-04 12:54:00 +00:00
|
|
|
|
|
|
|
memset(&sum, 0, sizeof(sum));
|
|
|
|
for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) {
|
|
|
|
sum.reset += priv_timer[lcore_id].stats.reset;
|
|
|
|
sum.stop += priv_timer[lcore_id].stats.stop;
|
|
|
|
sum.manage += priv_timer[lcore_id].stats.manage;
|
|
|
|
sum.pending += priv_timer[lcore_id].stats.pending;
|
|
|
|
}
|
2014-05-02 23:42:56 +00:00
|
|
|
fprintf(f, "Timer statistics:\n");
|
|
|
|
fprintf(f, " reset = %"PRIu64"\n", sum.reset);
|
|
|
|
fprintf(f, " stop = %"PRIu64"\n", sum.stop);
|
|
|
|
fprintf(f, " manage = %"PRIu64"\n", sum.manage);
|
|
|
|
fprintf(f, " pending = %"PRIu64"\n", sum.pending);
|
2012-09-04 12:54:00 +00:00
|
|
|
#else
|
2014-05-02 23:42:56 +00:00
|
|
|
fprintf(f, "No timer statistics, RTE_LIBRTE_TIMER_DEBUG is disabled\n");
|
2012-09-04 12:54:00 +00:00
|
|
|
#endif
|
|
|
|
}
|
2019-04-15 21:41:27 +00:00
|
|
|
|
|
|
|
void
|
|
|
|
rte_timer_dump_stats_v20(FILE *f)
|
|
|
|
{
|
|
|
|
__rte_timer_dump_stats(&default_timer_data, f);
|
|
|
|
}
|
|
|
|
VERSION_SYMBOL(rte_timer_dump_stats, _v20, 2.0);
|
|
|
|
|
|
|
|
int
|
|
|
|
rte_timer_dump_stats_v1905(FILE *f)
|
|
|
|
{
|
|
|
|
return rte_timer_alt_dump_stats(default_data_id, f);
|
|
|
|
}
|
|
|
|
MAP_STATIC_SYMBOL(int rte_timer_dump_stats(FILE *f),
|
|
|
|
rte_timer_dump_stats_v1905);
|
|
|
|
BIND_DEFAULT_SYMBOL(rte_timer_dump_stats, _v1905, 19.05);
|
|
|
|
|
2019-06-29 11:58:52 +00:00
|
|
|
int
|
2019-04-15 21:41:27 +00:00
|
|
|
rte_timer_alt_dump_stats(uint32_t timer_data_id __rte_unused, FILE *f)
|
|
|
|
{
|
|
|
|
struct rte_timer_data *timer_data;
|
|
|
|
|
|
|
|
TIMER_DATA_VALID_GET_OR_ERR_RET(timer_data_id, timer_data, -EINVAL);
|
|
|
|
|
|
|
|
__rte_timer_dump_stats(timer_data, f);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|