/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2010-2014 Intel Corporation
 */

#ifndef _RTE_TIMER_H_
#define _RTE_TIMER_H_

/**
 * @file
 RTE Timer
 *
 * This library provides a timer service to RTE Data Plane execution
 * units that allows the execution of callback functions asynchronously.
 *
 * - Timers can be periodic or single (one-shot).
 * - The timers can be loaded from one core and executed on another. This has
 *   to be specified in the call to rte_timer_reset().
 * - High precision is possible. NOTE: this depends on the call frequency to
 *   rte_timer_manage() that check the timer expiration for the local core.
 * - If not used in an application, for improved performance, it can be
 *   disabled at compilation time by not calling the rte_timer_manage()
 *   to improve performance.
 *
 * The timer library uses the rte_get_hpet_cycles() function that
 * uses the HPET, when available, to provide a reliable time reference. [HPET
 * routines are provided by EAL, which falls back to using the chip TSC (time-
 * stamp counter) as fallback when HPET is not available]
 *
 * This library provides an interface to add, delete and restart a
 * timer. The API is based on the BSD callout(9) API with a few
 * differences.
 *
 * See the RTE architecture documentation for more information about the
 * design of this library.
 */

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <rte_common.h>
#include <rte_config.h>
#include <rte_spinlock.h>

#ifdef __cplusplus
extern "C" {
#endif

#define RTE_TIMER_STOP    0 /**< State: timer is stopped. */
#define RTE_TIMER_PENDING 1 /**< State: timer is scheduled. */
#define RTE_TIMER_RUNNING 2 /**< State: timer function is running. */
#define RTE_TIMER_CONFIG  3 /**< State: timer is being configured. */

#define RTE_TIMER_NO_OWNER -2 /**< Timer has no owner. */

/**
 * Timer type: Periodic or single (one-shot).
 */
enum rte_timer_type {
	SINGLE,
	PERIODICAL
};

/**
 * Timer status: A union of the state (stopped, pending, running,
 * config) and an owner (the id of the lcore that owns the timer).
 */
union rte_timer_status {
	RTE_STD_C11
	struct {
		uint16_t state;  /**< Stop, pending, running, config. */
		int16_t owner;   /**< The lcore that owns the timer. */
	};
	uint32_t u32;            /**< To atomic-set status + owner. */
};

#ifdef RTE_LIBRTE_TIMER_DEBUG
/**
 * A structure that stores the timer statistics (per-lcore).
 */
struct rte_timer_debug_stats {
	uint64_t reset;   /**< Number of success calls to rte_timer_reset(). */
	uint64_t stop;    /**< Number of success calls to rte_timer_stop(). */
	uint64_t manage;  /**< Number of calls to rte_timer_manage(). */
	uint64_t pending; /**< Number of pending/running timers. */
};
#endif

struct rte_timer;

/**
 * Callback function type for timer expiry.
 */
typedef void (*rte_timer_cb_t)(struct rte_timer *, void *);

#define MAX_SKIPLIST_DEPTH 10

/**
 * A structure describing a timer in RTE.
 */
struct rte_timer
{
	uint64_t expire;       /**< Time when timer expire. */
	struct rte_timer *sl_next[MAX_SKIPLIST_DEPTH];
	volatile union rte_timer_status status; /**< Status of timer. */
	uint64_t period;       /**< Period of timer (0 if not periodic). */
	rte_timer_cb_t f;      /**< Callback function. */
	void *arg;             /**< Argument to callback function. */
};


#ifdef __cplusplus
/**
 * A C++ static initializer for a timer structure.
 */
#define RTE_TIMER_INITIALIZER {             \
	0,                                      \
	{NULL},                                 \
	{{RTE_TIMER_STOP, RTE_TIMER_NO_OWNER}}, \
	0,                                      \
	NULL,                                   \
	NULL,                                   \
	}
#else
/**
 * A static initializer for a timer structure.
 */
#define RTE_TIMER_INITIALIZER {                      \
		.status = {{                         \
			.state = RTE_TIMER_STOP,     \
			.owner = RTE_TIMER_NO_OWNER, \
		}},                                  \
	}
#endif

/**
 * Allocate a timer data instance in shared memory to track a set of pending
 * timer lists.
 *
 * @param id_ptr
 *   Pointer to variable into which to write the identifier of the allocated
 *   timer data instance.
 *
 * @return
 *   - 0: Success
 *   - -ENOSPC: maximum number of timer data instances already allocated
 */
int rte_timer_data_alloc(uint32_t *id_ptr);

/**
 * Deallocate a timer data instance.
 *
 * @param id
 *   Identifier of the timer data instance to deallocate.
 *
 * @return
 *   - 0: Success
 *   - -EINVAL: invalid timer data instance identifier
 */
int rte_timer_data_dealloc(uint32_t id);

/**
 * Initialize the timer library.
 *
 * Initializes internal variables (list, locks and so on) for the RTE
 * timer library.
 *
 * @note
 *   This function must be called in every process before using the library.
 *
 * @return
 *   - 0: Success
 *   - -ENOMEM: Unable to allocate memory needed to initialize timer
 *      subsystem
 *   - -EALREADY: timer subsystem was already initialized. Not an error.
 */
int rte_timer_subsystem_init(void);

/**
 * Free timer subsystem resources.
 */
void rte_timer_subsystem_finalize(void);

/**
 * Initialize a timer handle.
 *
 * The rte_timer_init() function initializes the timer handle *tim*
 * for use. No operations can be performed on a timer before it is
 * initialized.
 *
 * @param tim
 *   The timer to initialize.
 */
void rte_timer_init(struct rte_timer *tim);

/**
 * Reset and start the timer associated with the timer handle.
 *
 * The rte_timer_reset() function resets and starts the timer
 * associated with the timer handle *tim*. When the timer expires after
 * *ticks* HPET cycles, the function specified by *fct* will be called
 * with the argument *arg* on core *tim_lcore*.
 *
 * If the timer associated with the timer handle is already running
 * (in the RUNNING state), the function will fail. The user has to check
 * the return value of the function to see if there is a chance that the
 * timer is in the RUNNING state.
 *
 * If the timer is being configured on another core (the CONFIG state),
 * it will also fail.
 *
 * If the timer is pending or stopped, it will be rescheduled with the
 * new parameters.
 *
 * @param tim
 *   The timer handle.
 * @param ticks
 *   The number of cycles (see rte_get_hpet_hz()) before the callback
 *   function is called.
 * @param type
 *   The type can be either:
 *   - PERIODICAL: The timer is automatically reloaded after execution
 *     (returns to the PENDING state)
 *   - SINGLE: The timer is one-shot, that is, the timer goes to a
 *     STOPPED state after execution.
 * @param tim_lcore
 *   The ID of the lcore where the timer callback function has to be
 *   executed. If tim_lcore is LCORE_ID_ANY, the timer library will
 *   launch it on a different core for each call (round-robin).
 * @param fct
 *   The callback function of the timer.
 * @param arg
 *   The user argument of the callback function.
 * @return
 *   - 0: Success; the timer is scheduled.
 *   - (-1): Timer is in the RUNNING or CONFIG state.
 */
int rte_timer_reset(struct rte_timer *tim, uint64_t ticks,
		    enum rte_timer_type type, unsigned tim_lcore,
		    rte_timer_cb_t fct, void *arg);

/**
 * Loop until rte_timer_reset() succeeds.
 *
 * Reset and start the timer associated with the timer handle. Always
 * succeed. See rte_timer_reset() for details.
 *
 * @param tim
 *   The timer handle.
 * @param ticks
 *   The number of cycles (see rte_get_hpet_hz()) before the callback
 *   function is called.
 * @param type
 *   The type can be either:
 *   - PERIODICAL: The timer is automatically reloaded after execution
 *     (returns to the PENDING state)
 *   - SINGLE: The timer is one-shot, that is, the timer goes to a
 *     STOPPED state after execution.
 * @param tim_lcore
 *   The ID of the lcore where the timer callback function has to be
 *   executed. If tim_lcore is LCORE_ID_ANY, the timer library will
 *   launch it on a different core for each call (round-robin).
 * @param fct
 *   The callback function of the timer.
 * @param arg
 *   The user argument of the callback function.
 *
 * @note
 *   This API should not be called inside a timer's callback function to
 *   reset another timer; doing so could hang in certain scenarios. Instead,
 *   the rte_timer_reset() API can be called directly and its return code
 *   can be checked for success or failure.
 */
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);

/**
 * Stop a timer.
 *
 * The rte_timer_stop() function stops the timer associated with the
 * timer handle *tim*. It may fail if the timer is currently running or
 * being configured.
 *
 * If the timer is pending or stopped (for instance, already expired),
 * the function will succeed. The timer handle tim must have been
 * initialized using rte_timer_init(), otherwise, undefined behavior
 * will occur.
 *
 * This function can be called safely from a timer callback. If it
 * succeeds, the timer is not referenced anymore by the timer library
 * and the timer structure can be freed (even in the callback
 * function).
 *
 * @param tim
 *   The timer handle.
 * @return
 *   - 0: Success; the timer is stopped.
 *   - (-1): The timer is in the RUNNING or CONFIG state.
 */
int rte_timer_stop(struct rte_timer *tim);

/**
 * Loop until rte_timer_stop() succeeds.
 *
 * After a call to this function, the timer identified by *tim* is
 * stopped. See rte_timer_stop() for details.
 *
 * @param tim
 *   The timer handle.
 *
 * @note
 *   This API should not be called inside a timer's callback function to
 *   stop another timer; doing so could hang in certain scenarios. Instead, the
 *   rte_timer_stop() API can be called directly and its return code can
 *   be checked for success or failure.
 */
void rte_timer_stop_sync(struct rte_timer *tim);

/**
 * Test if a timer is pending.
 *
 * The rte_timer_pending() function tests the PENDING status
 * of the timer handle *tim*. A PENDING timer is one that has been
 * scheduled and whose function has not yet been called.
 *
 * @param tim
 *   The timer handle.
 * @return
 *   - 0: The timer is not pending.
 *   - 1: The timer is pending.
 */
int rte_timer_pending(struct rte_timer *tim);

/**
 * @warning
 * @b EXPERIMENTAL: this API may change without prior notice
 *
 * Time until the next timer on the current lcore
 * This function gives the ticks until the next timer will be active.
 *
 * @return
 *   - -EINVAL: invalid timer data instance identifier
 *   - -ENOENT: no timer pending
 *   - 0: a timer is pending and will run at next rte_timer_manage()
 *   - >0: ticks until the next timer is ready
 */
__rte_experimental
int64_t rte_timer_next_ticks(void);

/**
 * Manage the timer list and execute callback functions.
 *
 * This function must be called periodically from EAL lcores
 * main_loop(). It browses the list of pending timers and runs all
 * timers that are expired.
 *
 * The precision of the timer depends on the call frequency of this
 * function. However, the more often the function is called, the more
 * CPU resources it will use.
 *
 * @return
 *   - 0: Success
 *   - -EINVAL: timer subsystem not yet initialized
 */
int rte_timer_manage(void);

/**
 * Dump statistics about timers.
 *
 * @param f
 *   A pointer to a file for output
 * @return
 *   - 0: Success
 *   - -EINVAL: timer subsystem not yet initialized
 */
int rte_timer_dump_stats(FILE *f);

/**
 * This function is the same as rte_timer_reset(), except that it allows a
 * caller to specify the rte_timer_data instance containing the list to which
 * the timer should be added.
 *
 * @see rte_timer_reset()
 *
 * @param timer_data_id
 *   An identifier indicating which instance of timer data should be used for
 *   this operation.
 * @param tim
 *   The timer handle.
 * @param ticks
 *   The number of cycles (see rte_get_hpet_hz()) before the callback
 *   function is called.
 * @param type
 *   The type can be either:
 *   - PERIODICAL: The timer is automatically reloaded after execution
 *     (returns to the PENDING state)
 *   - SINGLE: The timer is one-shot, that is, the timer goes to a
 *     STOPPED state after execution.
 * @param tim_lcore
 *   The ID of the lcore where the timer callback function has to be
 *   executed. If tim_lcore is LCORE_ID_ANY, the timer library will
 *   launch it on a different core for each call (round-robin).
 * @param fct
 *   The callback function of the timer. This parameter can be NULL if (and
 *   only if) rte_timer_alt_manage() will be used to manage this timer.
 * @param arg
 *   The user argument of the callback function.
 * @return
 *   - 0: Success; the timer is scheduled.
 *   - (-1): Timer is in the RUNNING or CONFIG state.
 *   - -EINVAL: invalid timer_data_id
 */
int
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);

/**
 * This function is the same as rte_timer_stop(), except that it allows a
 * caller to specify the rte_timer_data instance containing the list from which
 * this timer should be removed.
 *
 * @see rte_timer_stop()
 *
 * @param timer_data_id
 *   An identifier indicating which instance of timer data should be used for
 *   this operation.
 * @param tim
 *   The timer handle.
 * @return
 *   - 0: Success; the timer is stopped.
 *   - (-1): The timer is in the RUNNING or CONFIG state.
 *   - -EINVAL: invalid timer_data_id
 */
int
rte_timer_alt_stop(uint32_t timer_data_id, struct rte_timer *tim);

/**
 * Callback function type for rte_timer_alt_manage().
 */
typedef void (*rte_timer_alt_manage_cb_t)(struct rte_timer *tim);

/**
 * Manage a set of timer lists and execute the specified callback function for
 * all expired timers. This function is similar to rte_timer_manage(), except
 * that it allows a caller to specify the timer_data instance that should
 * be operated on, as well as a set of lcore IDs identifying which timer lists
 * should be processed.  Callback functions of individual timers are ignored.
 *
 * @see rte_timer_manage()
 *
 * @param timer_data_id
 *   An identifier indicating which instance of timer data should be used for
 *   this operation.
 * @param poll_lcores
 *   An array of lcore ids identifying the timer lists that should be processed.
 *   NULL is allowed - if NULL, the timer list corresponding to the lcore
 *   calling this routine is processed (same as rte_timer_manage()).
 * @param n_poll_lcores
 *   The size of the poll_lcores array. If 'poll_lcores' is NULL, this parameter
 *   is ignored.
 * @param f
 *   The callback function which should be called for all expired timers.
 * @return
 *   - 0: success
 *   - -EINVAL: invalid timer_data_id
 */
int
rte_timer_alt_manage(uint32_t timer_data_id, unsigned int *poll_lcores,
		     int n_poll_lcores, rte_timer_alt_manage_cb_t f);

/**
 * Callback function type for rte_timer_stop_all().
 */
typedef void (*rte_timer_stop_all_cb_t)(struct rte_timer *tim, void *arg);

/**
 * Walk the pending timer lists for the specified lcore IDs, and for each timer
 * that is encountered, stop it and call the specified callback function to
 * process it further.
 *
 * @param timer_data_id
 *   An identifier indicating which instance of timer data should be used for
 *   this operation.
 * @param walk_lcores
 *   An array of lcore ids identifying the timer lists that should be processed.
 * @param nb_walk_lcores
 *   The size of the walk_lcores array.
 * @param f
 *   The callback function which should be called for each timers. Can be NULL.
 * @param f_arg
 *   An arbitrary argument that will be passed to f, if it is called.
 * @return
 *   - 0: success
 *   - EINVAL: invalid timer_data_id
 */
int
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);

/**
 * This function is the same as rte_timer_dump_stats(), except that it allows
 * the caller to specify the rte_timer_data instance that should be used.
 *
 * @see rte_timer_dump_stats()
 *
 * @param timer_data_id
 *   An identifier indicating which instance of timer data should be used for
 *   this operation.
 * @param f
 *   A pointer to a file for output
 * @return
 *   - 0: success
 *   - -EINVAL: invalid timer_data_id
 */
int
rte_timer_alt_dump_stats(uint32_t timer_data_id, FILE *f);

#ifdef __cplusplus
}
#endif

#endif /* _RTE_TIMER_H_ */
