49. Event Timer Adapter Library

The DPDK Event Device library introduces an event driven programming model which presents applications with an alternative to the polling model traditionally used in DPDK applications. Event devices can be coupled with arbitrary components to provide new event sources by using event adapters. The Event Timer Adapter is one such adapter; it bridges event devices and timer mechanisms.

The Event Timer Adapter library extends the event driven model by introducing a new type of event that represents a timer expiration, and providing an API with which adapters can be created or destroyed, and event timers can be armed and canceled.

The Event Timer Adapter library is designed to interface with hardware or software implementations of the timer mechanism; it will query an eventdev PMD to determine which implementation should be used. The default software implementation manages timers using the DPDK Timer library.

Examples of using the API are presented in the API Overview and Processing Timer Expiry Events sections. Code samples are abstracted and are based on the example of handling a TCP retransmission.

49.1. Event Timer struct

Event timers are timers that enqueue a timer expiration event to an event device upon timer expiration.

The Event Timer Adapter API represents each event timer with a generic struct, which contains an event and user metadata. The rte_event_timer struct is defined in lib/event/librte_event_timer_adapter.h.

49.1.1. Timer Expiry Event

The event contained by an event timer is enqueued in the event device when the timer expires, and the event device uses the attributes below when scheduling it:

  • event_queue_id - Application should set this to specify an event queue to which the timer expiry event should be enqueued
  • event_priority - Application can set this to indicate the priority of the timer expiry event in the event queue relative to other events
  • sched_type - Application can set this to specify the scheduling type of the timer expiry event
  • flow_id - Application can set this to indicate which flow this timer expiry event corresponds to
  • op - Will be set to RTE_EVENT_OP_NEW by the event timer adapter
  • event_type - Will be set to RTE_EVENT_TYPE_TIMER by the event timer adapter

49.1.2. Timeout Ticks

The number of ticks from now in which the timer will expire. The ticks value has a resolution (timer_tick_ns) that is specified in the event timer adapter configuration.

49.1.3. State

Before arming an event timer, the application should initialize its state to RTE_EVENT_TIMER_NOT_ARMED. The event timer’s state will be updated when a request to arm or cancel it takes effect.

If the application wishes to rearm the timer after it has expired, it should reset the state back to RTE_EVENT_TIMER_NOT_ARMED before doing so.

49.1.4. User Metadata

Memory to store user specific metadata. The event timer adapter implementation will not modify this area.

49.2. API Overview

This section will introduce the reader to the event timer adapter API, showing how to create and configure an event timer adapter and use it to manage event timers.

From a high level, the setup steps are:

  • rte_event_timer_adapter_create()
  • rte_event_timer_adapter_start()

And to start and stop timers:

  • rte_event_timer_arm_burst()
  • rte_event_timer_cancel_burst()

49.2.1. Create and Configure an Adapter Instance

To create an event timer adapter instance, initialize an rte_event_timer_adapter_conf struct with the desired values, and pass it to rte_event_timer_adapter_create().

#define NSECPERSEC 1E9
const struct rte_event_timer_adapter_conf adapter_config = {
        .event_dev_id = event_dev_id,
        .timer_adapter_id = 0,
        .socket_id = rte_socket_id(),
        .clk_src = RTE_EVENT_TIMER_ADAPTER_CPU_CLK,
        .timer_tick_ns = NSECPERSEC / 10,
        .max_tmo_ns = 180 * NSECPERSEC,
        .nb_timers = 40000,
        .flags = 0,
};

struct rte_event_timer_adapter *adapter;
adapter = rte_event_timer_adapter_create(&adapter_config);

if (adapter == NULL) { ... };

Before creating an instance of a timer adapter, the application should create and configure an event device along with its event ports. Based on the event device capability, it might require creating an additional event port to be used by the timer adapter. If required, the rte_event_timer_adapter_create() function will use a default method to configure an event port; it will examine the current event device configuration, determine the next available port identifier number, and create a new event port with a default port configuration.

If the application desires to have finer control of event port allocation and setup, it can use the rte_event_timer_adapter_create_ext() function. This function is passed a callback function that will be invoked if the adapter needs to create an event port, giving the application the opportunity to control how it is done.

49.2.1.1. Adapter modes

An event timer adapter can be configured in either periodic or non-periodic mode to support timers of the respective type. A periodic timer expires at a fixed time interval repeatedly till it is cancelled. A non-periodic timer expires only once. The periodic capability flag, RTE_EVENT_TIMER_ADAPTER_CAP_PERIODIC, can be set for implementations that support periodic mode if desired. To configure an adapter in periodic mode, flags of rte_event_timer_adapter_conf is set to include the periodic flag RTE_EVENT_TIMER_ADAPTER_F_PERIODIC. Maximum timeout (max_tmo_ns) does not apply to periodic mode.

49.2.2. Retrieve Event Timer Adapter Contextual Information

The event timer adapter implementation may have constraints on tick resolution or maximum timer expiry timeout based on the given event timer adapter or system. In this case, the implementation may adjust the tick resolution or maximum timeout to the best possible configuration.

Upon successful event timer adapter creation, the application can get the configured resolution and max timeout with rte_event_timer_adapter_get_info(). This function will return an rte_event_timer_adapter_info struct, which contains the following members:

  • min_resolution_ns - Minimum timer adapter tick resolution in ns.
  • max_tmo_ns - Maximum timer timeout(expiry) in ns.
  • adapter_conf - Configured event timer adapter attributes

49.2.3. Configuring the Service Component

If the adapter uses a service component, the application is required to map the service to a service core before starting the adapter:

uint32_t service_id;

if (rte_event_timer_adapter_service_id_get(adapter, &service_id) == 0)
        rte_service_map_lcore_set(service_id, EVTIM_CORE_ID);

An event timer adapter uses a service component if the event device PMD indicates that the adapter should use a software implementation.

49.2.4. Starting the Adapter Instance

The application should call rte_event_timer_adapter_start() to start running the event timer adapter. This function calls the start entry points defined by eventdev PMDs for hardware implementations or puts a service component into the running state in the software implementation.

Note

The eventdev to which the event_timer_adapter is connected needs to be started before calling rte_event_timer_adapter_start().

49.2.5. Arming Event Timers

Once an event timer adapter has been started, an application can begin to manage event timers with it.

The application should allocate struct rte_event_timer objects from a mempool or huge-page backed application buffers of required size. Upon successful allocation, the application should initialize the event timer, and then set any of the necessary event attributes described in the Timer Expiry Event section. In the following example, assume conn represents a TCP connection and that event_timer_pool is a mempool that was created previously:

rte_mempool_get(event_timer_pool, (void **)&conn->evtim);
if (conn->evtim == NULL) { ... }

/* Set up the event timer. */
conn->evtim->ev.op = RTE_EVENT_OP_NEW;
conn->evtim->ev.queue_id = event_queue_id;
conn->evtim->ev.sched_type = RTE_SCHED_TYPE_ATOMIC;
conn->evtim->ev.priority = RTE_EVENT_DEV_PRIORITY_NORMAL;
conn->evtim->ev.event_type = RTE_EVENT_TYPE_TIMER;
conn->evtim->ev.event_ptr = conn;
conn->evtim->state = RTE_EVENT_TIMER_NOT_ARMED;
conn->evtim->timeout_ticks = 30; //3 sec Per RFC1122(TCP returns)

Note that it is necessary to initialize the event timer state to RTE_EVENT_TIMER_NOT_ARMED. Also note that we have saved a pointer to the conn object in the timer’s event payload. This will allow us to locate the connection object again once we dequeue the timer expiry event from the event device later. As a convenience, the application may specify no value for ev.event_ptr, and the adapter will by default set it to point at the event timer itself.

Now we can arm the event timer with rte_event_timer_arm_burst():

ret = rte_event_timer_arm_burst(adapter, &conn->evtim, 1);
if (ret != 1) { ... }

Once an event timer expires, the application may free it or rearm it as necessary. If the application will rearm the timer, the state should be reset to RTE_EVENT_TIMER_NOT_ARMED by the application before rearming it. Timer expiry events will be generated once or periodically until the timer is cancelled based on adapter mode.

49.2.5.1. Multiple Event Timers with Same Expiry Value

In the special case that there is a set of event timers that should all expire at the same time, the application may call rte_event_timer_arm_tmo_tick_burst(), which allows the implementation to optimize the operation if possible.

49.2.6. Canceling Event Timers

An event timer that has been armed as described in Arming Event Timers can be canceled by calling rte_event_timer_cancel_burst():

/* Ack for the previous tcp data packet has been received;
 * cancel the retransmission timer
 */
rte_event_timer_cancel_burst(adapter, &conn->timer, 1);

49.3. Processing Timer Expiry Events

Once an event timer has successfully enqueued a timer expiry event in the event device, the application will subsequently dequeue it from the event device. The application can use the event payload to retrieve a pointer to the object associated with the event timer. It can then re-arm the event timer or free the event timer object as desired:

void
event_processing_loop(...)
{
        while (...) {
                /* Receive events from the configured event port. */
                rte_event_dequeue_burst(event_dev_id, event_port, &ev, 1, 0);
                ...
                switch(ev.event_type) {
                        ...
                        case RTE_EVENT_TYPE_TIMER:
                                process_timer_event(ev);
                                ...
                                break;
                }
        }
}

uint8_t
process_timer_event(...)
{
        /* A retransmission timeout for the connection has been received. */
        conn = ev.event_ptr;
        /* Retransmit last packet (e.g. TCP segment). */
        ...
        /* Re-arm timer using original values. */
        rte_event_timer_arm_burst(adapter_id, &conn->timer, 1);
}

49.4. Summary

The Event Timer Adapter library extends the DPDK event-based programming model by representing timer expirations as events in the system and allowing applications to use existing event processing loops to arm and cancel event timers or handle timer expiry events.