Discussion:
[dpdk-dev] [RFC PATCH 0/1] eventtimer: introduce event timer wheel
(too old to reply)
Jerin Jacob
2017-08-17 16:11:03 UTC
Permalink
Some of the NPU class of networking hardwares has timer hardware where the user
can arm and cancel the event timer. On the expiry of the timeout time, the
hardware will post the notification as an event to eventdev HW, Instead of
calling a callback like CPU based timer scheme. It enables, highresolution
(1us or so) timer management using internal or external clock domains, and
offloading the timer housing keeping work from the worker lcores.

This RFC attempts to abstract such NPU class of timer Hardware and introduce
event timer wheel subsystem inside the eventdev as they are tightly coupled.

This RFC introduces the functionality to create an event timer wheel. This
allows an application to arm event timers, which shall enqueue an event to a
specified event queue on expiry of a given interval.

The event timer wheel uses an ops table to which the various event devices
(e.g Cavium Octeontx, NXP dpaa2 and SW) register timer subsystem implementation
specific ops to use.

The RFC extends DPDK event based programming model where event can be of type
timer, and expiry event will be notified through CPU over eventdev ports.

Some of the use cases of event timer wheel are Beacon Timers,
Generic SW Timeout, Wireless MAC Scheduling, 3G Frame Protocols,
Packet Scheduling, Protocol Retransmission Timers, Supervision Timers.
All these use cases require high resolution and low time drift.

The abstract working model of an event timer wheel is as follows:
=================================================================
timer_tick_ns
+
+-------+ |
| | |
+-------+ bkt 0 +----v---+
| | | |
| +-------+ |
+---+---+ +---+---+ +---+---+---+---+
| | | | | | | | |
| bkt n | | bkt 1 |<-> t0| t1| t2| tn|
| | | | | | | | |
+---+---+ +---+---+ +---+---+---+---+
| Timer wheel |
+---+---+ +---+---+
| | | |
| bkt 4 | | bkt 2 |<--- Current bucket
| | | |
+---+---+ +---+---+
| +-------+ |
| | | |
+------+ bkt 3 +-------+
| |
+-------+

- It has a virtual monotonically increasing 64-bit timer wheel clock based on
*enum rte_event_timer_wheel_clk_src* clock source. The clock source could
be a CPU clock, or a platform depended external clock.

- Application creates a timer wheel instance with given clock source,
the total number of event timers, resolution(expressed in ns) to traverse
between the buckets.

- Each timer wheel may have 0 to n buckets based on the configured
max timeout(max_tmo_ns) and resolution(timer_tick_ns). On timer wheel
start, the timer starts ticking at *timer_tick_ns* resolution.

- Application arms an event timer to be expired at the number of
*timer_tick_ns* from now.

- Application can cancel the existing armed timer if required.

- If not canceled by the application and the timer expires then the library
injects the timer expiry event to the designated event queue.

- The timer expiry event will be received through *rte_event_dequeue_burst*

- Application frees the created timer wheel instance.

A more detailed description of the event timer wheel is contained in the
header's comments.

Implementation thoughts
=======================
The event devices have to provide a driver level function that is used to get
event timer subsystem capability and the respective event timer wheel ops.
if the event device is not capable a software implementation of the event timer
wheel ops will be selected.

The software implementation of timer wheel will make use of existing
rte_timer[1], rte_ring library and EAL service cores[2] to achieve event
generation. The worker cores call event timer arm function which enqueues event
timer to a rte_ring. The registered service core would then dequeue event timer
from rte_ring and use the rte_timer library to register a timer. The service
core then invokes rte_timer_manage() function to retrieve expired timers and
generates the associated event.

The implementation of event timer wheel subsystem for both hardware (Cavium
OCTEONTX) and software(if there are no volunteers) will be undertaken by Cavium.

[1] http://dpdk.org/doc/guides/prog_guide/timer_lib.html
[2] http://dpdk.org/ml/archives/dev/2017-May/065207.html

An example code snippet to show the proposed API usage
======================================================
example: TCP Retransmission in abstract form.

uint8_t
configure_event_dev(...)
{
/* Create the event device. */
const struct rte_event_dev_config config = {
.nb_event_queues = 1,
/* Event device related configuration. */
...
};

rte_event_dev_configure(event_dev_id, &config);
/* Event queue and port configuration. */
...
/* Start the event device.*/
rte_event_dev_start(event_dev_id);
}

#define NSECPERSEC 1E9 // No of ns for 1 sec
uint8_t
configure_event_timer_wheel(...)
{
/* Create an event timer wheel for reliable connections. */
const struct rte_event_timer_wheel_config wheel_config = {
.event_dev_id = event_dev_id,
.timer_wheel_id = 0,
.clk_src = RTE_EVENT_TIMER_WHEEL_CPU_CLK,
.timer_tick_ns = NSECPERSEC / 10, // 100 milliseconds
.max_tmo_nsec = 180 * NSECPERSEC // 2 minutes
.nb_timers = 40000, // Number of timers that the wheel can hold.
.timer_wheel_flags = 0,
};
struct rte_event_timer_wheel *wheel = NULL;
wheel = rte_event_timer_wheel_create(&wheel_config);
if (wheel == NULL)
{
/* Failed to create event timer wheel. */
...
return false;

}
/* Start the event timer wheel. */
rte_event_timer_wheel_start(wheel);

/* Create a mempool of event timers. */
struct rte_mempool *event_timer_pool = NULL;

event_timer_pool = rte_mempool_create("event_timer_mempool", SIZE,
sizeof(struct rte_event_timer), ...);
if (event_timer_pool == NULL)
{
/* Failed to create event timer mempool. */
...
return false;
}
}


uint8_t
process_tcp_data_packet(...)
{
/*Classify based on type*/
switch (...) {
case ...:
/* Setting up a new connection (Protocol dependent.) */
...
/* Setting up a new event timer. */
conn->timer = NULL
rte_mempool_get(event_timer_pool, (void **)&conn->timer);
if (timer == NULL) {
/* Failed to get event timer instance. */
/* Tear down the connection */
return false;
}

/* Set up the timer event. */
conn->timer->ev.u64 = conn;
conn->timer->ev.queue_id = event_queue_id;
...
/* All necessary resources successfully allocated */
/* Compute the timer timeout ticks */
conn->timer->timeout_ticks = 30; //3 sec Per RFC1122(TCP returns)
/* Arm the timer with our timeout */
ret = rte_event_timer_arm_burst(wheel, &conn->timer, 1);
if (ret != 1) {
/* Check return value for too early or too late expiration
* tick */
...
return false;
}
return true;
case ...:
/* Ack for the previous tcp data packet has been received.*/
/* cancel the retransmission timer*/
rte_event_timer_cancel_burst(wheel, &conn->timer, 1);
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(wheel_id, &conn->timer, 1);
}

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

int main()
{

configure_event_dev();
configure_event_timer_wheel();
on_each_worker_lcores(events_processing_loop())
}

Jerin Jacob (1):
eventtimer: introduce event timer wheel

doc/api/doxy-api-index.md | 3 +-
lib/librte_eventdev/Makefile | 1 +
lib/librte_eventdev/rte_event_timer_wheel.h | 493 ++++++++++++++++++++++++++++
lib/librte_eventdev/rte_eventdev.h | 4 +-
4 files changed, 498 insertions(+), 3 deletions(-)
create mode 100644 lib/librte_eventdev/rte_event_timer_wheel.h
--
2.14.1
Jerin Jacob
2017-08-17 16:11:04 UTC
Permalink
Some of the NPU class of networking hardwares has timer hardware where the user
can arm and cancel the event timer. On the expiry of the timeout time, the
hardware will post the notification as an event to eventdev HW, Instead of
calling a callback like CPU based timer scheme. It enables, high resolution
(1us or so) timer management using internal or external clock domains, and
offloading the timer housing keeping work from the worker lcores.

This RFC attempts to abstract such NPU class of timer Hardware and introduce
event timer wheel subsystem inside the eventdev as they are tightly coupled.

This RFC introduces the functionality to create an event timer wheel. This
allows an application to arm event timers, which shall enqueue an event to a
specified event queue on expiry of a given interval.

The event timer wheel uses an ops table to which the various event devices
(e.g Cavium Octeontx, NXP dpaa2 and SW) register timer subsystem implementation
specific ops to use.

The RFC extends DPDK event based programming model where event can be of type
timer, and expiry event will be notified through CPU over eventdev ports.

Some of the use cases of event timer wheel are Beacon Timers,
Generic SW Timeout, Wireless MAC Scheduling, 3G Frame Protocols,
Packet Scheduling, Protocol Retransmission Timers, Supervision Timers.
All these use cases require high resolution and low time drift.

Signed-off-by: Jerin Jacob <***@caviumnetworks.com>
Signed-off-by: Pavan Nikhilesh <***@caviumnetworks.com>
---
doc/api/doxy-api-index.md | 3 +-
lib/librte_eventdev/Makefile | 1 +
lib/librte_eventdev/rte_event_timer_wheel.h | 493 ++++++++++++++++++++++++++++
lib/librte_eventdev/rte_eventdev.h | 4 +-
4 files changed, 498 insertions(+), 3 deletions(-)
create mode 100644 lib/librte_eventdev/rte_event_timer_wheel.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 19e0d4f3d..6b8b6338a 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -55,7 +55,8 @@ The public API headers are grouped by topics:
[KNI] (@ref rte_kni.h),
[ixgbe] (@ref rte_pmd_ixgbe.h),
[i40e] (@ref rte_pmd_i40e.h),
- [crypto_scheduler] (@ref rte_cryptodev_scheduler.h)
+ [crypto_scheduler] (@ref rte_cryptodev_scheduler.h),
+ [event_timerwheel] (@ref rte_event_timer_wheel.h)

- **memory**:
[memseg] (@ref rte_memory.h),
diff --git a/lib/librte_eventdev/Makefile b/lib/librte_eventdev/Makefile
index 410578a14..ce1c128f5 100644
--- a/lib/librte_eventdev/Makefile
+++ b/lib/librte_eventdev/Makefile
@@ -50,6 +50,7 @@ SYMLINK-y-include += rte_eventdev_pmd.h
SYMLINK-y-include += rte_eventdev_pmd_pci.h
SYMLINK-y-include += rte_eventdev_pmd_vdev.h
SYMLINK-y-include += rte_event_ring.h
+SYMLINK-y-include += rte_event_timer_wheel.h

# versioning export map
EXPORT_MAP := rte_eventdev_version.map
diff --git a/lib/librte_eventdev/rte_event_timer_wheel.h b/lib/librte_eventdev/rte_event_timer_wheel.h
new file mode 100644
index 000000000..f568d2adf
--- /dev/null
+++ b/lib/librte_eventdev/rte_event_timer_wheel.h
@@ -0,0 +1,493 @@
+/*
+ * BSD LICENSE
+ *
+ * Copyright 2017 Cavium, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Cavium, Inc nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_EVENT_TIMER_WHEEL_H_
+#define _RTE_EVENT_TIMER_WHEEL_H_
+
+/**
+ * @file
+ *
+ * RTE event timer wheel
+ *
+ * The abstract working model of an event timer wheel is as follows:
+ *
+ * timer_tick_ns
+ * +
+ * +-------+ |
+ * | | |
+ * +-------+ bkt 0 +----v---+
+ * | | | |
+ * | +-------+ |
+ * +---+---+ +---+---+ +---+---+---+---+
+ * | | | | | | | | |
+ * | bkt n | | bkt 1 |<-> t0| t1| t2| tn|
+ * | | | | | | | | |
+ * +---+---+ +---+---+ +---+---+---+---+
+ * | Timer wheel |
+ * +---+---+ +---+---+
+ * | | | |
+ * | bkt 4 | | bkt 2 |<--- Current bucket
+ * | | | |
+ * +---+---+ +---+---+
+ * | +-------+ |
+ * | | | |
+ * +------+ bkt 3 +-------+
+ * | |
+ * +-------+
+ *
+ * - It has a virtual monotonically increasing 64-bit timer wheel clock based on
+ * *enum rte_event_timer_wheel_clk_src* clock source. The clock source could
+ * be a CPU clock, or a platform depended external clock.
+ *
+ * - Application creates a timer wheel instance with given clock source,
+ * the total number of event timers, resolution(expressed in ns) to traverse
+ * between the buckets.
+ *
+ * - Each timer wheel may have 0 to n buckets based on the configured
+ * max timeout(max_tmo_ns) and resolution(timer_tick_ns). On timer wheel
+ * start, the timer starts ticking at *timer_tick_ns* resolution.
+ *
+ * - Application arms an event timer to be expired at the number of
+ * *timer_tick_ns* from now.
+ *
+ * - Application can cancel the existing armed timer if required.
+ *
+ * - If not canceled by the application and the timer expires then the library
+ * injects the timer expiry event to the designated event queue.
+ *
+ * - The timer expiry event will be received through *rte_event_dequeue_burst*
+ *
+ * - Application frees the created timer wheel instance.
+ *
+ * Multiple timer wheels can be created with a varying level of resolution
+ * for various expiry use cases that run in parallel.
+ *
+ * Before using the timer wheel, the application has to create and configure
+ * an event device along with the event ports. Based on the event device
+ * capability it might require creating an additional event port to be used
+ * by the timer wheel.
+ *
+ * The application creates the event timer wheel using the
+ * ``rte_event_timer_wheel_create()``. The event device id is passed to this
+ * function, inside this function the event device capability is checked,
+ * and if an in-built port is absent the application uses the <default>
+ * function to create a new producer port.
+ *
+ * The application may also use the function
+ * ``rte_event_timer_wheel_create_ext()`` to have granular control over producer
+ * port creation in a case where the in-built port is absent.
+ *
+ * After creating the timer wheel, the application has to start it
+ * using ``rte_event_timer_wheel_start()``. The buckets are traversed from
+ * 0 to n, the list per each bucket is processed, and the expired timer events
+ * are sent to the designated event queue.
+ *
+ * The application can arm one or more event timers using the
+ * ``rte_event_timer_arm_burst()``. The *timeout_ticks* represents the number
+ * of *timer_tick_ns* after which the timer has to expire. The timeout at
+ * which the timers expire can be grouped or be independent of each
+ * event timer instance. ``rte_event_timer_arm_tmo_tick_burst()`` address the
+ * former case and ``rte_event_timer_arm_burst()`` address the latter case.
+ *
+ * The application can cancel the timers from expiring using the
+ * ``rte_event_timer_cancel_burst()``.
+ *
+ * On the secondary process, ``rte_event_timer_wheel_lookup()`` can be used to
+ * get the timer wheel pointer from its id and use it to invoke fastpath
+ * operations such as arm and cancel.
+ *
+ * Some of the use cases of event timer wheel are Beacon Timers,
+ * Generic SW Timeout, Wireless MAC Scheduling, 3G Frame Protocols,
+ * Packet Scheduling, Protocol Retransmission Timers, Supervision Timers.
+ * All these use cases require high resolution and low time drift.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_eventdev.h>
+
+/**
+ * Timer wheel clock source.
+ */
+enum rte_event_timer_wheel_clk_src {
+ RTE_EVENT_TIMER_WHEEL_CPU_CLK,
+ /**< Use CPU clock as the clock source. */
+ RTE_EVENT_TIMER_WHEEL_EXT_CLK0,
+ /**< Platform dependent external clock source 0. */
+ RTE_EVENT_TIMER_WHEEL_EXT_CLK1,
+ /**< Platform dependent external clock source 1. */
+ RTE_EVENT_TIMER_WHEEL_EXT_CLK2,
+ /**< Platform dependent external clock source 2. */
+ RTE_EVENT_TIMER_WHEEL_EXT_CLK3,
+ /**< Platform dependent external clock source 3. */
+};
+
+#define RTE_EVENT_TIMER_WHEEL_F_ADJUST_RES (1ULL << 0)
+/**< The event timer wheel implementation may have constraints on the wheel
+ * resolution(timer_tick_ns) and maximum timer expiry timeout(max_tmo_ns)
+ * based on the given event timer wheel or system.
+ * If this flag is set, implementation adjusts the wheel resolution and
+ * maximum timeout to the best possible configuration.
+ * On successful timer wheel creation, the application can get the configured
+ * resolution and max timeout with ``rte_event_timer_wheel_get_info()``.
+ *
+ * @see struct rte_event_timer_wheel_conf::timer_wheel_flags
+ */
+#define RTE_EVENT_TIMER_WHEEL_F_SP_PUT (1ULL << 1)
+/**< ``rte_event_timer_arm_burst()`` API to be used in single producer mode.
+ *
+ * @see struct rte_event_timer_wheel_conf::timer_wheel_flags
+ */
+
+/**
+ * Timer wheel configuration structure.
+ */
+struct rte_event_timer_wheel_conf {
+ uint8_t event_dev_id;
+ /**< Event device identifier.*/
+ uint16_t timer_wheel_id;
+ /**< The event timer wheel identifier */
+ enum rte_event_timer_wheel_clk_src clk_src;
+ /**< Clock source for timer wheel. */
+ uint64_t timer_tick_ns;
+ /**< Timer wheel resolution in ns i.e interval between buckets.*/
+ uint64_t max_tmo_ns;
+ /**< Maximum timer timeout(expiry) in ns.*/
+ uint64_t nb_timers;
+ /**< Total number of timers per wheel.*/
+ uint32_t timer_wheel_flags;
+ /**< Timer wheel config flags(RTE_EVENT_TIMER_WHEEL_F_*).*/
+};
+
+struct rte_event_timer_wheel;
+
+/**
+ * Create an event timer wheel.
+ *
+ * This function must be invoked first before any other function in the API.
+ *
+ * @param conf
+ * The event timer wheel configuration structure.
+ *
+ * @return
+ * The pointer to the new allocated event timer wheel, on success.
+ * NULL on error with rte_errno set appropriately.
+ * Possible rte_errno values include:
+ * - ERANGE - timer_tick_ns is not in supported range.
+ */
+struct rte_event_timer_wheel *
+rte_event_timer_wheel_create(const struct rte_event_timer_wheel_conf *conf);
+
+/**
+ * Callback function type for producer port creation.
+ */
+typedef void (*rte_event_timer_wheel_port_conf_cb)(uint16_t id,
+ uint8_t event_dev_id, uint8_t *event_port_id,
+ void *conf_arg);
+
+/**
+ * Create an timer wheel with supplied callback.
+ *
+ * This function can be used to have a more granular control over the timer
+ * wheel creation, If an inbuilt port is absent then the function uses the
+ * callback provided to create and get the port id to be used as producer port.
+ *
+ *
+ * @param[out] wheel
+ * A pointer to the event timer wheel structure pointer.
+ * @param conf
+ * The timer wheel configuration structure.
+ * @param conf_cb
+ * The port config callback function.
+ * @param conf_arg
+ * Opaque pointer to the callback function.
+ *
+ * @return
+ * The pointer to the new allocated event timer wheel, on success.
+ * NULL on error with rte_errno set appropriately.
+ * Possible rte_errno values include:
+ * - ERANGE - timer_tick_ns is not in supported range.
+ */
+struct rte_event_timer_wheel *
+rte_event_timer_wheel_create_ext(const struct rte_event_timer_wheel_conf *conf,
+ rte_event_timer_wheel_port_conf_cb conf_cb, void *conf_arg);
+
+/**
+ * Timer wheel info structure.
+ */
+struct rte_event_timer_wheel_info {
+ uint64_t min_resoultion_ns;
+ /**< Minimum timer wheel resolution in ns.*/
+ uint64_t max_tmo_ns;
+ /**< Maximum timer timeout(expiry) in ns. */
+ struct rte_event_timer_wheel_conf wheel_conf;
+ /**< Configured timer wheel attributes. */
+};
+
+/**
+ * Retrieve the contextual information of an event timer wheel.
+ *
+ * @param wheel
+ * A pointer to the event timer wheel structure.
+ *
+ * @param[out] wheel_info
+ * A pointer to a structure of type *rte_event_timer_wheel_info* to be
+ * filled with the contextual information of the wheel.
+ *
+ * @return
+ * - 0: Success, driver updates the contextual information of the
+ * timer wheel
+ * - <0: Error code returned by the driver info get function.
+ *
+ * @see RTE_EVENT_TIMER_WHEEL_F_ADJUST_RES, struct rte_event_timer_wheel_info
+ *
+ */
+int
+rte_event_timer_wheel_get_info(const struct rte_event_timer_wheel *wheel,
+ struct rte_event_timer_wheel_info *wheel_info);
+
+/**
+ * Start an timer wheel.
+ *
+ * The wheel start step is the last one and consists of setting the timer
+ * wheel to start accepting the timers and schedules to event queues.
+ *
+ * On success, all basic functions exported by the API (timer arm,
+ * timer cancel and so on) can be invoked.
+ *
+ * @param wheel
+ * A pointer to the event timer wheel structure.
+ *
+ * @return
+ * - 0: Success, wheel started.
+ * - <0: Error code returned by the driver start function.
+ */
+int
+rte_event_timer_wheel_start(const struct rte_event_timer_wheel *wheel);
+
+/**
+ * Stop an event timer wheel.
+ *
+ * The wheel can be restarted with a call to ``rte_event_timer_wheel_start()``.
+ *
+ * @param wheel
+ * A pointer to the event timer wheel structure.
+ *
+ * @return
+ * - 0: Success, wheel stop.
+ * - <0: Error code returned by the driver stop function.
+ */
+int
+rte_event_timer_wheel_stop(const struct rte_event_timer_wheel *wheel);
+
+/**
+ * Free an event timer wheel.
+ *
+ * Destroy an event timer wheel, freeing all resources.
+ *
+ * Before invoking this function, the application must wait for all the armed
+ * timers to expire or cancel the outstanding armed timers.
+ *
+ * @param wheel
+ * A pointer to the event timer wheel structure.
+ *
+ * @return
+ * - 0 on successfully freed the event timer wheel resources.
+ * - <0 on failure to free an event timer wheel.
+ * - -EAGAIN if wheel is busy
+ */
+int
+rte_event_timer_wheel_free(const struct rte_event_timer_wheel *wheel);
+
+/**
+ * Search an event timer wheel from its id.
+ *
+ * @param timer_wheel_id
+ * The event timer wheel identifier.
+ *
+ * @return
+ * The pointer to the event timer wheel matching the id, or NULL if not found.
+ * NULL on error with rte_errno set appropriately.
+ * Possible rte_errno values include:
+ * - ENOENT - required entry not available to return.
+ */
+struct rte_event_timer_wheel *
+rte_event_timer_wheel_lookup(uint16_t timer_wheel_id);
+
+/**
+ * Event timer state.
+ */
+enum rte_event_timer_state {
+ RTE_EVENT_TIMER_NOT_ARMED = 0,
+ /**< Event timer is in not armed state.*/
+ RTE_EVENT_TIMER_SUCCESS_ARM = 1,
+ /**< Event timer successfully armed.*/
+ RTE_EVENT_TIMER_SUCCESS_CANCEL = 2,
+ /**< Event timer successfully canceled.*/
+ RTE_EVENT_TIMER_ERROR = -1,
+ /**< Generic event timer error.*/
+ RTE_EVENT_TIMER_ERROR_TOOEARLY = -2,
+ /**< Event timer timeout tick is too less to add to the wheel.*/
+ RTE_EVENT_TIMER_ERROR_TOOLATE = -3,
+ /**< Event timer timeout tick is greater than the maximum timeout.*/
+};
+
+/**
+ * The generic *rte_event_timer* structure to hold the event timer attributes
+ * for arm and cancel operations.
+ */
+RTE_STD_C11
+struct rte_event_timer {
+ struct rte_event ev;
+ /**<
+ * Expiry event attributes. On successful event timer timeout,following
+ * attributes will be used to inject the expiry event to eventdev.
+ * - event_queue_id: Targeted event queue id for expiry events.
+ * - event_priority: Event priority of the event expiry event in the
+ * event queue relative to other events.
+ * - sched_type: Scheduling type of the expiry event.
+ * - flow_id: Flow id of the expiry event.
+ * - op: RTE_EVENT_OP_NEW
+ * - event_type: RTE_EVENT_TYPE_TIMER
+ */
+ enum rte_event_timer_state state;
+ /**< State of the event timer.*/
+ uint64_t timeout_ticks;
+ /**< Expiry timer ticks expressed in number of *timer_ticks_ns* from
+ * now.
+ * @see struct rte_event_timer_wheel_info::wheel_conf::timer_tick_ns
+ */
+ uint64_t impl_opaque[2];
+ /**< Implementation specific opaque data.
+ * An event timer wheel implementation may use this field to hold
+ * implementation specific value to share between the arm and cancel
+ * operation. The application should not modify this field.
+ */
+ uint8_t user_meta[];
+ /**< Memory to store user specific metadata.
+ * The event timer wheel implementation should not modify this area.
+ */
+} __rte_cache_aligned;
+
+/**
+ * Arm a burst of event timers with separate expiration timeout tick for each
+ * event timer.
+ *
+ * Before calling this function, the application allocates
+ * ``struct rte_event_timer`` objects from mempool or huge page backed
+ * application buffers of interested size. On successful allocation,
+ * application updates the `struct rte_event_timer`` attributes such as
+ * expiry event attributes, timeout ticks from now.
+ * This function submits the event timer arm requests to the event timer wheel
+ * and on expiry, the events will be injected to designated event queue.
+ *
+ * @param wheel
+ * A pointer to the event timer wheel structure.
+ * @param tim
+ * Points to an array of objects of type *rte_event_timer* structure.
+ * @param nb_timers
+ * Number of event timers in the supplied array.
+ *
+ * @return
+ * The number of successfully armed event timers. The return value can be less
+ * than the value of the *nb_timers* parameter. If the return value is less
+ * than *nb_events*, the remaining event timers at the end of *tim*
+ * are not consumed, and the caller has to take care of them, and rte_errno
+ * is set accordingly. Possible errno values include:
+ * - -EINVAL Invalid timer wheel pointer, expiry event queue ID is invalid,
+ * or an expiry event's sched type doesn't match the capabilities of the
+ * destination event queue.
+ */
+int
+rte_event_timer_arm_burst(const struct rte_event_timer_wheel *wheel,
+ struct rte_event_timer **tim, const uint16_t nb_timers);
+
+/**
+ * Arm a burst of event timers with same expiration timeout tick.
+ *
+ * Provides the same functionality as ``rte_event_timer_arm_burst()``, expect
+ * that application can use this API when the all the event timers have the
+ * same timeout expiration tick. This specialized function can provide the
+ * additional hint to the wheel implementation and optimize if possible.
+ *
+ * @param wheel
+ * A pointer to the event timer wheel structure.
+ * @param tim
+ * Points to an array of objects of type *rte_event_timer* structure.
+ * @param nb_timers
+ * Number of event timers in the supplied array.
+ *
+ * @return
+ * The number of successfully armed event timers. The return value can be less
+ * than the value of the *nb_timers* parameter. If the return value is less
+ * than *nb_events*, the remaining event timers at the end of *tim*
+ * are not consumed, and the caller has to take care of them, and rte_errno
+ * is set accordingly. Possible errno values include:
+ * - -EINVAL Invalid timer wheel pointer, expiry event queue ID is invalid,
+ * or an expiry event's sched type doesn't match the capabilities of the
+ * destination event queue.
+ */
+int
+rte_event_timer_arm_tmo_tick_burst(const struct rte_event_timer_wheel *wheel,
+ struct rte_event_timer **tim, const uint64_t timeout_tick,
+ const uint16_t nb_timers);
+
+
+/**
+ * Cancel a burst of event timer from being scheduled to the event device.
+ *
+ * @param wheel
+ * A pointer to the event timer wheel structure.
+ * @param tim
+ * Points to an array of objects of type *rte_event_timer* structure
+ * @param nb_timers
+ * Number of event timer instances in the supplied array.
+ *
+ * @return
+ * The number of successfully canceled event timers. The return value can be
+ * less than the value of the *nb_timers* parameter. If the return value is
+ * less than *nb_events*, the remaining event timers at the end of *tim*
+ * are not consumed, and the caller has to take care of them, and rte_errno
+ * is set accordingly. Possible errno values include:
+ * - -EINVAL Invalid timer wheel pointer
+ */
+int
+rte_event_timer_cancel_burst(const struct rte_event_timer_wheel *wheel,
+ struct rte_event_timer **tim, const uint16_t nb_timers);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_EVENT_TIMER_WHEEL_H_ */
diff --git a/lib/librte_eventdev/rte_eventdev.h b/lib/librte_eventdev/rte_eventdev.h
index 128bc5221..219ec5a3a 100644
--- a/lib/librte_eventdev/rte_eventdev.h
+++ b/lib/librte_eventdev/rte_eventdev.h
@@ -865,8 +865,8 @@ rte_event_dev_close(uint8_t dev_id);
/**< The event generated from ethdev subsystem */
#define RTE_EVENT_TYPE_CRYPTODEV 0x1
/**< The event generated from crypodev subsystem */
-#define RTE_EVENT_TYPE_TIMERDEV 0x2
-/**< The event generated from timerdev subsystem */
+#define RTE_EVENT_TYPE_TIMER 0x2
+/**< The event generated from event timer wheel */
#define RTE_EVENT_TYPE_CPU 0x3
/**< The event generated from cpu for pipelining.
* Application may use *sub_event_type* to further classify the event
--
2.14.1
Carrillo, Erik G
2017-08-23 22:57:08 UTC
Permalink
Hi Jerin,

Thanks for sharing your proposal.

We have implemented something quite similar locally. In applications that utilize the eventdev framework, entities we call "bridge drivers" are configured, and they are analogous to service cores.

One such bridge driver, the Timer Bridge Driver, runs on an lcore specified during application startup and, once it is started, will manage set of event timers, and enqueue timer events into an event device upon their expiry. To use event timers, the application will allocate them and set a payload pointer and queue id in pretty much the same way you've shown. Then we call rte_event_timer_reset() to arm the timer, which will install it in one of the rte_timer library's skiplists. Concurrently, the Timer Bridge Driver will be executing a run() function in a loop, which will repeatedly execute rte_timer_manage(). For any timers that have expired, a callback defined in the bridge driver will execute, and this callback will enqueue a new event of type TIMER. As workers are dequeuing events, they will encounter the timer event and can use the "payload pointer" to get back to some specified object.

Some differences include:
- our API doesn't currently support burst-style arming
- our API supports periodic timers (since they are free from the rte_timer lib)

Regarding the implementation you describe in the "Implementation thoughts" section of your email, our timer bridge driver doesn't have a ring in which it enqueues timer events. Instead the rte_event_timer_reset() is mostly a wrapper for rte_timer_reset(). Because various worker lcores could be modifying timers concurrently, and they were all headed for the same skiplist, we encountered performance issues with lock contention for that skiplist. I modified the timer library itself in various ways, including adding a multiple-producer single-consumer ring for requests to modify the skiplists. I had the best results however, when I created per-installer skiplists for each target lcore. I submitted the patch to the ML today[1]. I personally saw contention on the CAS operation to update the head pointer of ring when multiple lcores installed timers repeatedly and simultaneously, but perhaps burst-enqueuing can avoid that.

On a separate note, it also looks like attributes of the timer wheel pertaining to resolution or max number of timers will have no effect in the software implementation. Instead, timer resolution would be a function of the frequency with which a service core can invoke rte_timer_manage. Also, it would seem that no limit on the number of timers would be necessary. Does that sound right?

In summary, it looks like our solutions align fairly well, and I propose that we take on the software implementation if there are no objections.

[1] http://dpdk.org/ml/archives/dev/2017-August/073317.html

Thanks,
Gabriel
-----Original Message-----
Sent: Thursday, August 17, 2017 11:11 AM
Subject: [dpdk-dev] [RFC PATCH 0/1] eventtimer: introduce event timer
wheel
Some of the NPU class of networking hardwares has timer hardware where
the user can arm and cancel the event timer. On the expiry of the timeout
time, the hardware will post the notification as an event to eventdev HW,
Instead of calling a callback like CPU based timer scheme. It enables,
highresolution (1us or so) timer management using internal or external clock
domains, and offloading the timer housing keeping work from the worker
lcores.
This RFC attempts to abstract such NPU class of timer Hardware and
introduce event timer wheel subsystem inside the eventdev as they are
tightly coupled.
This RFC introduces the functionality to create an event timer wheel. This
allows an application to arm event timers, which shall enqueue an event to a
specified event queue on expiry of a given interval.
The event timer wheel uses an ops table to which the various event devices
(e.g Cavium Octeontx, NXP dpaa2 and SW) register timer subsystem
implementation specific ops to use.
The RFC extends DPDK event based programming model where event can be
of type timer, and expiry event will be notified through CPU over eventdev
ports.
Some of the use cases of event timer wheel are Beacon Timers, Generic SW
Timeout, Wireless MAC Scheduling, 3G Frame Protocols, Packet Scheduling,
Protocol Retransmission Timers, Supervision Timers.
All these use cases require high resolution and low time drift.
==========================================================
=======
timer_tick_ns
+
+-------+ |
| | |
+-------+ bkt 0 +----v---+
| | | |
| +-------+ |
+---+---+ +---+---+ +---+---+---+---+
| | | | | | | | |
| bkt n | | bkt 1 |<-> t0| t1| t2| tn|
| | | | | | | | |
+---+---+ +---+---+ +---+---+---+---+
| Timer wheel |
+---+---+ +---+---+
| | | |
| bkt 4 | | bkt 2 |<--- Current bucket
| | | |
+---+---+ +---+---+
| +-------+ |
| | | |
+------+ bkt 3 +-------+
| |
+-------+
- It has a virtual monotonically increasing 64-bit timer wheel clock based on
*enum rte_event_timer_wheel_clk_src* clock source. The clock source could
be a CPU clock, or a platform depended external clock.
- Application creates a timer wheel instance with given clock source,
the total number of event timers, resolution(expressed in ns) to traverse
between the buckets.
- Each timer wheel may have 0 to n buckets based on the configured
max timeout(max_tmo_ns) and resolution(timer_tick_ns). On timer wheel
start, the timer starts ticking at *timer_tick_ns* resolution.
- Application arms an event timer to be expired at the number of
*timer_tick_ns* from now.
- Application can cancel the existing armed timer if required.
- If not canceled by the application and the timer expires then the library
injects the timer expiry event to the designated event queue.
- The timer expiry event will be received through
*rte_event_dequeue_burst*
- Application frees the created timer wheel instance.
A more detailed description of the event timer wheel is contained in the
header's comments.
Implementation thoughts
=======================
The event devices have to provide a driver level function that is used to get
event timer subsystem capability and the respective event timer wheel ops.
if the event device is not capable a software implementation of the event
timer wheel ops will be selected.
The software implementation of timer wheel will make use of existing
rte_timer[1], rte_ring library and EAL service cores[2] to achieve event
generation. The worker cores call event timer arm function which enqueues
event timer to a rte_ring. The registered service core would then dequeue
event timer from rte_ring and use the rte_timer library to register a timer.
The service core then invokes rte_timer_manage() function to retrieve
expired timers and generates the associated event.
The implementation of event timer wheel subsystem for both hardware (Cavium
OCTEONTX) and software(if there are no volunteers) will be undertaken by Cavium.
[1] http://dpdk.org/doc/guides/prog_guide/timer_lib.html
[2] http://dpdk.org/ml/archives/dev/2017-May/065207.html
An example code snippet to show the proposed API usage
======================================================
example: TCP Retransmission in abstract form.
uint8_t
configure_event_dev(...)
{
/* Create the event device. */
const struct rte_event_dev_config config = {
.nb_event_queues = 1,
/* Event device related configuration. */
...
};
rte_event_dev_configure(event_dev_id, &config);
/* Event queue and port configuration. */
...
/* Start the event device.*/
rte_event_dev_start(event_dev_id);
}
#define NSECPERSEC 1E9 // No of ns for 1 sec
uint8_t
configure_event_timer_wheel(...)
{
/* Create an event timer wheel for reliable connections. */
const struct rte_event_timer_wheel_config wheel_config = {
.event_dev_id = event_dev_id,
.timer_wheel_id = 0,
.clk_src = RTE_EVENT_TIMER_WHEEL_CPU_CLK,
.timer_tick_ns = NSECPERSEC / 10, // 100 milliseconds
.max_tmo_nsec = 180 * NSECPERSEC // 2 minutes
.nb_timers = 40000, // Number of timers that the wheel can
hold.
.timer_wheel_flags = 0,
};
struct rte_event_timer_wheel *wheel = NULL;
wheel = rte_event_timer_wheel_create(&wheel_config);
if (wheel == NULL)
{
/* Failed to create event timer wheel. */
...
return false;
}
/* Start the event timer wheel. */
rte_event_timer_wheel_start(wheel);
/* Create a mempool of event timers. */
struct rte_mempool *event_timer_pool = NULL;
event_timer_pool =
rte_mempool_create("event_timer_mempool", SIZE,
sizeof(struct rte_event_timer), ...);
if (event_timer_pool == NULL)
{
/* Failed to create event timer mempool. */
...
return false;
}
}
uint8_t
process_tcp_data_packet(...)
{
/*Classify based on type*/
switch (...) {
/* Setting up a new connection (Protocol dependent.) */
...
/* Setting up a new event timer. */
conn->timer = NULL
rte_mempool_get(event_timer_pool, (void **)&conn-
Post by Jerin Jacob
timer);
if (timer == NULL) {
/* Failed to get event timer instance. */
/* Tear down the connection */
return false;
}
/* Set up the timer event. */
conn->timer->ev.u64 = conn;
conn->timer->ev.queue_id = event_queue_id;
...
/* All necessary resources successfully allocated */
/* Compute the timer timeout ticks */
conn->timer->timeout_ticks = 30; //3 sec Per RFC1122(TCP
returns)
/* Arm the timer with our timeout */
ret = rte_event_timer_arm_burst(wheel, &conn->timer, 1);
if (ret != 1) {
/* Check return value for too early or too late
expiration
* tick */
...
return false;
}
return true;
/* Ack for the previous tcp data packet has been received.*/
/* cancel the retransmission timer*/
rte_event_timer_cancel_burst(wheel, &conn->timer, 1);
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(wheel_id, &conn->timer, 1); }
void
events_processing_loop(...)
{
while (...) {
/* Receive events from the configured event port. */
rte_event_dequeue_burst(event_dev_id, event_port, &ev,
1, 0);
...
/* Classify events based on event_type. */
switch(ev.event_type) {
...
process_packets(...);
break;
process_timer_event(ev);
...
break;
}
}
}
int main()
{
configure_event_dev();
configure_event_timer_wheel();
on_each_worker_lcores(events_processing_loop())
}
eventtimer: introduce event timer wheel
doc/api/doxy-api-index.md | 3 +-
lib/librte_eventdev/Makefile | 1 +
lib/librte_eventdev/rte_event_timer_wheel.h | 493
++++++++++++++++++++++++++++
lib/librte_eventdev/rte_eventdev.h | 4 +-
4 files changed, 498 insertions(+), 3 deletions(-) create mode 100644
lib/librte_eventdev/rte_event_timer_wheel.h
--
2.14.1
Jerin Jacob
2017-08-25 10:25:38 UTC
Permalink
-----Original Message-----
Date: Wed, 23 Aug 2017 22:57:08 +0000
Subject: RE: [dpdk-dev] [RFC PATCH 0/1] eventtimer: introduce event timer
wheel
Hi Jerin,
Hi Carrillo,
Thanks for sharing your proposal.
We have implemented something quite similar locally. In applications that utilize the eventdev framework, entities we call "bridge drivers" are configured, and they are analogous to service cores.
OK.
One such bridge driver, the Timer Bridge Driver, runs on an lcore specified during application startup and, once it is started, will manage set of event timers, and enqueue timer events into an event device upon their expiry. To use event timers, the application will allocate them and set a payload pointer and queue id in pretty much the same way you've shown. Then we call rte_event_timer_reset() to arm the timer, which will install it in one of the rte_timer library's skiplists. Concurrently, the Timer Bridge Driver will be executing a run() function in a loop, which will repeatedly execute rte_timer_manage(). For any timers that have expired, a callback defined in the bridge driver will execute, and this callback will enqueue a new event of type TIMER. As workers are dequeuing events, they will encounter the timer event and can use the "payload pointer" to get back to some specified object.
OK
- our API doesn't currently support burst-style arming
- our API supports periodic timers (since they are free from the rte_timer lib)
Regarding the implementation you describe in the "Implementation thoughts" section of your email, our timer bridge driver doesn't have a ring in which it enqueues timer events. Instead the rte_event_timer_reset() is mostly a wrapper for rte_timer_reset(). Because various worker lcores could be modifying timers concurrently, and they were all headed for the same skiplist, we encountered performance issues with lock contention for that skiplist. I modified the timer library itself in various ways, including adding a multiple-producer single-consumer ring for requests to modify the skiplists. I had the best results however, when I created per-installer skiplists for each target lcore. I submitted the patch to the ML today[1]. I personally saw contention on the CAS operation to update the head pointer of ring when multiple lcores installed timers repeatedly and simultaneously, but perhaps burst-enqueuing can avoid that.
OK
On a separate note, it also looks like attributes of the timer wheel pertaining to resolution or max number of timers will have no effect in the software implementation. Instead, timer resolution would be a function of the frequency with which a service core can invoke rte_timer_manage. Also, it would seem that no limit on the number of timers would be necessary. Does that sound right?
Yes. The SW implementation can ignore the resolution and max number of
timers. This is just hint from application to partition the HW resource
effectively.
In summary, it looks like our solutions align fairly well, and I propose that we take on the software implementation if there are no objections.
Sure, no objection.

Could you please comment on the API changes required in the header file.
Once it is finalized. We need to have
a) common code for ops
b) SW driver
c) HW driver

and we would like contribute on (a) and (c)

/Jerin
[1] http://dpdk.org/ml/archives/dev/2017-August/073317.html
Thanks,
Gabriel
-----Original Message-----
Sent: Thursday, August 17, 2017 11:11 AM
Subject: [dpdk-dev] [RFC PATCH 0/1] eventtimer: introduce event timer
wheel
Some of the NPU class of networking hardwares has timer hardware where
the user can arm and cancel the event timer. On the expiry of the timeout
time, the hardware will post the notification as an event to eventdev HW,
Instead of calling a callback like CPU based timer scheme. It enables,
highresolution (1us or so) timer management using internal or external clock
domains, and offloading the timer housing keeping work from the worker
lcores.
This RFC attempts to abstract such NPU class of timer Hardware and
introduce event timer wheel subsystem inside the eventdev as they are
tightly coupled.
This RFC introduces the functionality to create an event timer wheel. This
allows an application to arm event timers, which shall enqueue an event to a
specified event queue on expiry of a given interval.
The event timer wheel uses an ops table to which the various event devices
(e.g Cavium Octeontx, NXP dpaa2 and SW) register timer subsystem
implementation specific ops to use.
The RFC extends DPDK event based programming model where event can be
of type timer, and expiry event will be notified through CPU over eventdev
ports.
Some of the use cases of event timer wheel are Beacon Timers, Generic SW
Timeout, Wireless MAC Scheduling, 3G Frame Protocols, Packet Scheduling,
Protocol Retransmission Timers, Supervision Timers.
All these use cases require high resolution and low time drift.
==========================================================
=======
timer_tick_ns
+
+-------+ |
| | |
+-------+ bkt 0 +----v---+
| | | |
| +-------+ |
+---+---+ +---+---+ +---+---+---+---+
| | | | | | | | |
| bkt n | | bkt 1 |<-> t0| t1| t2| tn|
| | | | | | | | |
+---+---+ +---+---+ +---+---+---+---+
| Timer wheel |
+---+---+ +---+---+
| | | |
| bkt 4 | | bkt 2 |<--- Current bucket
| | | |
+---+---+ +---+---+
| +-------+ |
| | | |
+------+ bkt 3 +-------+
| |
+-------+
- It has a virtual monotonically increasing 64-bit timer wheel clock based on
*enum rte_event_timer_wheel_clk_src* clock source. The clock source could
be a CPU clock, or a platform depended external clock.
- Application creates a timer wheel instance with given clock source,
the total number of event timers, resolution(expressed in ns) to traverse
between the buckets.
- Each timer wheel may have 0 to n buckets based on the configured
max timeout(max_tmo_ns) and resolution(timer_tick_ns). On timer wheel
start, the timer starts ticking at *timer_tick_ns* resolution.
- Application arms an event timer to be expired at the number of
*timer_tick_ns* from now.
- Application can cancel the existing armed timer if required.
- If not canceled by the application and the timer expires then the library
injects the timer expiry event to the designated event queue.
- The timer expiry event will be received through
*rte_event_dequeue_burst*
- Application frees the created timer wheel instance.
A more detailed description of the event timer wheel is contained in the
header's comments.
Implementation thoughts
=======================
The event devices have to provide a driver level function that is used to get
event timer subsystem capability and the respective event timer wheel ops.
if the event device is not capable a software implementation of the event
timer wheel ops will be selected.
The software implementation of timer wheel will make use of existing
rte_timer[1], rte_ring library and EAL service cores[2] to achieve event
generation. The worker cores call event timer arm function which enqueues
event timer to a rte_ring. The registered service core would then dequeue
event timer from rte_ring and use the rte_timer library to register a timer.
The service core then invokes rte_timer_manage() function to retrieve
expired timers and generates the associated event.
The implementation of event timer wheel subsystem for both hardware (Cavium
OCTEONTX) and software(if there are no volunteers) will be undertaken by Cavium.
[1] http://dpdk.org/doc/guides/prog_guide/timer_lib.html
[2] http://dpdk.org/ml/archives/dev/2017-May/065207.html
An example code snippet to show the proposed API usage
======================================================
example: TCP Retransmission in abstract form.
uint8_t
configure_event_dev(...)
{
/* Create the event device. */
const struct rte_event_dev_config config = {
.nb_event_queues = 1,
/* Event device related configuration. */
...
};
rte_event_dev_configure(event_dev_id, &config);
/* Event queue and port configuration. */
...
/* Start the event device.*/
rte_event_dev_start(event_dev_id);
}
#define NSECPERSEC 1E9 // No of ns for 1 sec
uint8_t
configure_event_timer_wheel(...)
{
/* Create an event timer wheel for reliable connections. */
const struct rte_event_timer_wheel_config wheel_config = {
.event_dev_id = event_dev_id,
.timer_wheel_id = 0,
.clk_src = RTE_EVENT_TIMER_WHEEL_CPU_CLK,
.timer_tick_ns = NSECPERSEC / 10, // 100 milliseconds
.max_tmo_nsec = 180 * NSECPERSEC // 2 minutes
.nb_timers = 40000, // Number of timers that the wheel can
hold.
.timer_wheel_flags = 0,
};
struct rte_event_timer_wheel *wheel = NULL;
wheel = rte_event_timer_wheel_create(&wheel_config);
if (wheel == NULL)
{
/* Failed to create event timer wheel. */
...
return false;
}
/* Start the event timer wheel. */
rte_event_timer_wheel_start(wheel);
/* Create a mempool of event timers. */
struct rte_mempool *event_timer_pool = NULL;
event_timer_pool =
rte_mempool_create("event_timer_mempool", SIZE,
sizeof(struct rte_event_timer), ...);
if (event_timer_pool == NULL)
{
/* Failed to create event timer mempool. */
...
return false;
}
}
uint8_t
process_tcp_data_packet(...)
{
/*Classify based on type*/
switch (...) {
/* Setting up a new connection (Protocol dependent.) */
...
/* Setting up a new event timer. */
conn->timer = NULL
rte_mempool_get(event_timer_pool, (void **)&conn-
Post by Jerin Jacob
timer);
if (timer == NULL) {
/* Failed to get event timer instance. */
/* Tear down the connection */
return false;
}
/* Set up the timer event. */
conn->timer->ev.u64 = conn;
conn->timer->ev.queue_id = event_queue_id;
...
/* All necessary resources successfully allocated */
/* Compute the timer timeout ticks */
conn->timer->timeout_ticks = 30; //3 sec Per RFC1122(TCP
returns)
/* Arm the timer with our timeout */
ret = rte_event_timer_arm_burst(wheel, &conn->timer, 1);
if (ret != 1) {
/* Check return value for too early or too late
expiration
* tick */
...
return false;
}
return true;
/* Ack for the previous tcp data packet has been received.*/
/* cancel the retransmission timer*/
rte_event_timer_cancel_burst(wheel, &conn->timer, 1);
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(wheel_id, &conn->timer, 1); }
void
events_processing_loop(...)
{
while (...) {
/* Receive events from the configured event port. */
rte_event_dequeue_burst(event_dev_id, event_port, &ev,
1, 0);
...
/* Classify events based on event_type. */
switch(ev.event_type) {
...
process_packets(...);
break;
process_timer_event(ev);
...
break;
}
}
}
int main()
{
configure_event_dev();
configure_event_timer_wheel();
on_each_worker_lcores(events_processing_loop())
}
eventtimer: introduce event timer wheel
doc/api/doxy-api-index.md | 3 +-
lib/librte_eventdev/Makefile | 1 +
lib/librte_eventdev/rte_event_timer_wheel.h | 493
++++++++++++++++++++++++++++
lib/librte_eventdev/rte_eventdev.h | 4 +-
4 files changed, 498 insertions(+), 3 deletions(-) create mode 100644
lib/librte_eventdev/rte_event_timer_wheel.h
--
2.14.1
Thomas Monjalon
2017-08-29 15:02:43 UTC
Permalink
[...]
Post by Jerin Jacob
Post by Carrillo, Erik G
In summary, it looks like our solutions align fairly well, and I propose that we take on the software implementation if there are no objections.
Sure, no objection.
Good to see such a basic function generalized for NPU and CPU.

Are you going to use rte_timer for CPU implementation?
Does it mean that event_timer supersedes rte_timer?
Jerin Jacob
2017-08-29 15:41:29 UTC
Permalink
-----Original Message-----
Date: Tue, 29 Aug 2017 17:02:43 +0200
Subject: Re: [dpdk-dev] [RFC PATCH 0/1] eventtimer: introduce event timer
wheel
[...]
Post by Jerin Jacob
Post by Carrillo, Erik G
In summary, it looks like our solutions align fairly well, and I propose that we take on the software implementation if there are no objections.
Sure, no objection.
Good to see such a basic function generalized for NPU and CPU.
Are you going to use rte_timer for CPU implementation?
Does it mean that event_timer supersedes rte_timer?
IMO, we don't need to supersedes the rte_timer. The eventdev or event_timer is
an optional component. It is application decision to use poll mode vs event
driver model or combination of two.
Thomas Monjalon
2017-08-29 15:48:58 UTC
Permalink
Post by Jerin Jacob
Post by Thomas Monjalon
[...]
Post by Jerin Jacob
Post by Carrillo, Erik G
In summary, it looks like our solutions align fairly well, and I propose that we take on the software implementation if there are no objections.
Sure, no objection.
Good to see such a basic function generalized for NPU and CPU.
Are you going to use rte_timer for CPU implementation?
Does it mean that event_timer supersedes rte_timer?
IMO, we don't need to supersedes the rte_timer. The eventdev or event_timer is
an optional component. It is application decision to use poll mode vs event
driver model or combination of two.
Trying to make clear when using poll mode or event model,
regarding CPU/NPU differences:
If using poll mode + rte_timer, we cannot leverage NPU offloads.
If using event model, is it as much efficient for generic CPU?
Jerin Jacob
2017-08-29 16:07:06 UTC
Permalink
-----Original Message-----
Date: Tue, 29 Aug 2017 17:48:58 +0200
Subject: Re: [dpdk-dev] [RFC PATCH 0/1] eventtimer: introduce event timer
wheel
Post by Jerin Jacob
Post by Thomas Monjalon
[...]
Post by Jerin Jacob
Post by Carrillo, Erik G
In summary, it looks like our solutions align fairly well, and I propose that we take on the software implementation if there are no objections.
Sure, no objection.
Good to see such a basic function generalized for NPU and CPU.
Are you going to use rte_timer for CPU implementation?
Does it mean that event_timer supersedes rte_timer?
IMO, we don't need to supersedes the rte_timer. The eventdev or event_timer is
an optional component. It is application decision to use poll mode vs event
driver model or combination of two.
Trying to make clear when using poll mode or event model,
If using poll mode + rte_timer, we cannot leverage NPU offloads.
Yes.
If using event model, is it as much efficient for generic CPU?
It is case by case. I think, ATOMIC queues and PARALLEL queues can be
effectively implemented for the generic CPU. ORDERED queue(the reordering
business) may not very effective. So it is boiled down to application
requirements.
Erik Gabriel Carrillo
2017-09-22 15:17:10 UTC
Permalink
Hi Jerin, et al.,

The following RFC patch contains enough of a skeleton implementation
for an event timer adapter to show how the API could call through an ops
structure to a plugin backend to provide different implementations.

In terms of differences from the original RFC, the most obvious one is
that I've replaced occurrences of "wheel" in the API with "adapter",
partly to reflect the similarity with the event_eth_rx_adapter, and
partly because "wheel" suggests an implementation that may not be
the one used (as in the case of the SW impl).

The second big change is to replace API parameters specifying pointer
to rte_event_timer_adapter with ids, which seems more common throughout
DPDK.

Other changes include:
- removed rte_event_timer_adapter_lookup() function, since APIs no longer
accept pointer to rte_event_timer_adapter
- Replaced RTE_EVENT_TIMER_SUCCESS_{ARM,CANCEL} states with
RTE_EVENT_TIMER_ARMED, since the purpose of the SUCCESS_CANCEL state was
unclear

Please have a look and let me know what you think.

Thanks,
Gabriel


Erik Gabriel Carrillo (1):
eventtimer: introduce event timer adapter

drivers/event/sw/Makefile | 1 +
drivers/event/sw/sw_evdev.c | 11 +
drivers/event/sw/sw_evdev_timer_adapter.c | 124 +++++++
lib/librte_eventdev/Makefile | 3 +
lib/librte_eventdev/rte_event_timer_adapter.c | 227 ++++++++++++
lib/librte_eventdev/rte_event_timer_adapter.h | 388 +++++++++++++++++++++
.../rte_event_timer_adapter_driver.h | 128 +++++++
lib/librte_eventdev/rte_eventdev.h | 2 +-
lib/librte_eventdev/rte_eventdev_pmd.h | 15 +
lib/librte_eventdev/rte_eventdev_version.map | 14 +
test/test/Makefile | 1 +
test/test/test_event_timer_adapter.c | 183 ++++++++++
12 files changed, 1096 insertions(+), 1 deletion(-)
create mode 100644 drivers/event/sw/sw_evdev_timer_adapter.c
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter.c
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter.h
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter_driver.h
create mode 100644 test/test/test_event_timer_adapter.c
--
2.6.4
Erik Gabriel Carrillo
2017-09-22 15:17:11 UTC
Permalink
Signed-off-by: Erik Gabriel Carrillo <***@intel.com>
---
v2
- Added ops structure and stubbed out plugin for SW impl
- Added unit test stubs
- Replaced occurrences of "wheel" in API with "adapter"
- Replaced occurrences of pointer to struct rte_event_timer_adapter with ids
- Removed rte_event_timer_adapter_lookup() function
- Replaced RTE_EVENT_TIMER_SUCCESS_{ARM,CANCEL} states with
RTE_EVENT_TIMER_ARMED

drivers/event/sw/Makefile | 1 +
drivers/event/sw/sw_evdev.c | 11 +
drivers/event/sw/sw_evdev_timer_adapter.c | 124 +++++++
lib/librte_eventdev/Makefile | 3 +
lib/librte_eventdev/rte_event_timer_adapter.c | 227 ++++++++++++
lib/librte_eventdev/rte_event_timer_adapter.h | 388 +++++++++++++++++++++
.../rte_event_timer_adapter_driver.h | 128 +++++++
lib/librte_eventdev/rte_eventdev.h | 2 +-
lib/librte_eventdev/rte_eventdev_pmd.h | 15 +
lib/librte_eventdev/rte_eventdev_version.map | 14 +
test/test/Makefile | 1 +
test/test/test_event_timer_adapter.c | 183 ++++++++++
12 files changed, 1096 insertions(+), 1 deletion(-)
create mode 100644 drivers/event/sw/sw_evdev_timer_adapter.c
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter.c
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter.h
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter_driver.h
create mode 100644 test/test/test_event_timer_adapter.c

diff --git a/drivers/event/sw/Makefile b/drivers/event/sw/Makefile
index 857a87c..f059915 100644
--- a/drivers/event/sw/Makefile
+++ b/drivers/event/sw/Makefile
@@ -55,6 +55,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV) += sw_evdev.c
SRCS-$(CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV) += sw_evdev_worker.c
SRCS-$(CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV) += sw_evdev_scheduler.c
SRCS-$(CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV) += sw_evdev_xstats.c
+SRCS-$(CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV) += sw_evdev_timer_adapter.c

# export include files
SYMLINK-y-include +=
diff --git a/drivers/event/sw/sw_evdev.c b/drivers/event/sw/sw_evdev.c
index 9c534b7..bbbf80a 100644
--- a/drivers/event/sw/sw_evdev.c
+++ b/drivers/event/sw/sw_evdev.c
@@ -49,6 +49,8 @@
#define SCHED_QUANTA_ARG "sched_quanta"
#define CREDIT_QUANTA_ARG "credit_quanta"

+extern const struct rte_event_timer_adapter_ops sw_event_adapter_timer_ops;
+
static void
sw_info_get(struct rte_eventdev *dev, struct rte_event_dev_info *info);

@@ -733,6 +735,13 @@ static int32_t sw_sched_service_func(void *args)
}

static int
+sw_event_timer_adapter_ctrl(void *arg)
+{
+ *(const void **)arg = &sw_event_adapter_timer_ops;
+ return 0;
+}
+
+static int
sw_probe(struct rte_vdev_device *vdev)
{
static const struct rte_eventdev_ops evdev_sw_ops = {
@@ -756,6 +765,8 @@ sw_probe(struct rte_vdev_device *vdev)
.xstats_get_names = sw_xstats_get_names,
.xstats_get_by_name = sw_xstats_get_by_name,
.xstats_reset = sw_xstats_reset,
+
+ .event_timer_adapter_ctrl = sw_event_timer_adapter_ctrl,
};

static const char *const args[] = {
diff --git a/drivers/event/sw/sw_evdev_timer_adapter.c b/drivers/event/sw/sw_evdev_timer_adapter.c
new file mode 100644
index 0000000..5edab0a
--- /dev/null
+++ b/drivers/event/sw/sw_evdev_timer_adapter.c
@@ -0,0 +1,124 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016-2017 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdbool.h>
+
+#include <rte_event_timer_adapter.h>
+#include <rte_event_timer_adapter_driver.h>
+#include <rte_service_component.h>
+#include <rte_service.h>
+#include <rte_log.h>
+
+static int
+event_timer_adapter_service_func(void *args)
+{
+ RTE_SET_USED(args);
+
+ return 0;
+}
+
+static int
+start_event_timer_adapter_service(struct rte_event_timer_adapter *adapter,
+ uint8_t id)
+{
+ RTE_SET_USED(adapter);
+ RTE_SET_USED(id);
+
+ RTE_SET_USED(event_timer_adapter_service_func);
+
+ return 0;
+}
+
+static int
+sw_event_timer_adapter_start(struct rte_event_timer_adapter *adapter)
+{
+ RTE_SET_USED(adapter);
+
+ RTE_SET_USED(start_event_timer_adapter_service);
+
+ return 0;
+}
+
+static int
+sw_event_timer_adapter_stop(struct rte_event_timer_adapter *adapter)
+{
+ RTE_SET_USED(adapter);
+
+ return 0;
+}
+
+static int
+sw_event_timer_arm_burst(struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ const uint16_t nb_tims)
+{
+ RTE_SET_USED(adapter);
+ RTE_SET_USED(tims);
+ RTE_SET_USED(nb_tims);
+
+ return 0;
+}
+
+static int
+sw_event_timer_arm_tmo_tick_burst(struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ const uint64_t timeout_tick,
+ const uint16_t nb_tims)
+{
+ RTE_SET_USED(adapter);
+ RTE_SET_USED(tims);
+ RTE_SET_USED(timeout_tick);
+ RTE_SET_USED(nb_tims);
+
+ return 0;
+}
+
+static int
+sw_event_timer_cancel_burst(struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ const uint16_t nb_tims)
+{
+ RTE_SET_USED(adapter);
+ RTE_SET_USED(tims);
+ RTE_SET_USED(nb_tims);
+
+ return 0;
+}
+
+const struct rte_event_timer_adapter_ops sw_event_adapter_timer_ops = {
+ .adapter_start = sw_event_timer_adapter_start,
+ .adapter_stop = sw_event_timer_adapter_stop,
+ .event_timer_arm_burst = sw_event_timer_arm_burst,
+ .event_timer_arm_tmo_tick_burst = sw_event_timer_arm_tmo_tick_burst,
+ .event_timer_cancel_burst = sw_event_timer_cancel_burst,
+};
diff --git a/lib/librte_eventdev/Makefile b/lib/librte_eventdev/Makefile
index 410578a..b0904fe 100644
--- a/lib/librte_eventdev/Makefile
+++ b/lib/librte_eventdev/Makefile
@@ -43,6 +43,7 @@ CFLAGS += $(WERROR_FLAGS)
# library source files
SRCS-y += rte_eventdev.c
SRCS-y += rte_event_ring.c
+SRCS-y += rte_event_timer_adapter.c

# export include files
SYMLINK-y-include += rte_eventdev.h
@@ -50,6 +51,8 @@ SYMLINK-y-include += rte_eventdev_pmd.h
SYMLINK-y-include += rte_eventdev_pmd_pci.h
SYMLINK-y-include += rte_eventdev_pmd_vdev.h
SYMLINK-y-include += rte_event_ring.h
+SYMLINK-y-include += rte_event_timer_adapter.h
+SYMLINK-y-include += rte_event_timer_adapter_driver.h

# versioning export map
EXPORT_MAP := rte_eventdev_version.map
diff --git a/lib/librte_eventdev/rte_event_timer_adapter.c b/lib/librte_eventdev/rte_event_timer_adapter.c
new file mode 100644
index 0000000..d28c676
--- /dev/null
+++ b/lib/librte_eventdev/rte_event_timer_adapter.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+#include <rte_lcore.h>
+#include <rte_log.h>
+#include <rte_service_component.h>
+#include <rte_branch_prediction.h>
+
+#include "rte_eventdev_pmd.h"
+#include "rte_event_timer_adapter.h"
+#include "rte_event_timer_adapter_driver.h"
+
+#define RTE_EVENT_TIMER_ADAPTER_NUM_MAX 64
+
+static struct rte_event_timer_adapter *rte_event_timer_adapters;
+
+static inline int
+adapter_valid(uint16_t id)
+{
+ RTE_SET_USED(id);
+
+ return -1;
+}
+
+/* validate ID and retrieve adapter pointer, or return error value */
+#define ADAPTER_VALID_GET_OR_ERR_RET(id, adapter, retval) do { \
+ if (id >= RTE_EVENT_TIMER_ADAPTER_NUM_MAX || !adapter_valid(id)) \
+ return retval; \
+ adapter = &rte_event_timer_adapters[id]; \
+} while (0)
+
+
+static int
+default_port_conf_cb(uint16_t id, uint8_t event_dev_id, uint8_t event_port_id,
+ void *conf_arg)
+{
+ RTE_SET_USED(id);
+ RTE_SET_USED(event_dev_id);
+ RTE_SET_USED(event_port_id);
+ RTE_SET_USED(conf_arg);
+
+ return 0;
+}
+
+int
+rte_event_timer_adapter_create(struct rte_event_timer_adapter_conf *conf)
+{
+ /* TODO: check default values */
+ struct rte_event_port_conf port_conf = {
+ .new_event_threshold = 128,
+ .dequeue_depth = 32,
+ .enqueue_depth = 32
+ };
+
+ return rte_event_timer_adapter_create_ext(conf, default_port_conf_cb,
+ &port_conf);
+}
+
+int
+rte_event_timer_adapter_create_ext(struct rte_event_timer_adapter_conf *conf,
+ rte_event_timer_adapter_port_conf_cb_t conf_cb,
+ void *conf_arg)
+{
+ RTE_SET_USED(conf);
+ RTE_SET_USED(conf_cb);
+ RTE_SET_USED(conf_arg);
+
+ return 0;
+}
+
+int
+rte_event_timer_adapter_get_info(uint16_t id,
+ struct rte_event_timer_adapter_info *adapter_info)
+{
+
+ RTE_SET_USED(id);
+ RTE_SET_USED(adapter_info);
+
+ return 0;
+}
+
+int
+rte_event_timer_adapter_start(uint16_t id)
+{
+ const struct rte_event_timer_adapter_ops *ops = NULL;
+ struct rte_event_timer_adapter *adapter;
+
+ ADAPTER_VALID_GET_OR_ERR_RET(id, adapter, -EINVAL);
+
+ ops = rte_event_timer_adapter_ops_get(adapter);
+ if (unlikely(!ops))
+ return -1;
+ if (likely(!!ops->adapter_start))
+ return ops->adapter_start(adapter);
+
+ return -1;
+}
+
+int
+rte_event_timer_adapter_stop(uint16_t id)
+{
+ const struct rte_event_timer_adapter_ops *ops = NULL;
+ struct rte_event_timer_adapter *adapter;
+
+ ADAPTER_VALID_GET_OR_ERR_RET(id, adapter, -EINVAL);
+
+ ops = rte_event_timer_adapter_ops_get(adapter);
+ if (unlikely(!ops))
+ return -1;
+ if (likely(!!ops->adapter_stop))
+ return ops->adapter_stop(adapter);
+
+ return -1;
+}
+
+int
+rte_event_timer_adapter_free(uint16_t id)
+{
+ RTE_SET_USED(id);
+ return 0;
+}
+
+const struct rte_event_timer_adapter_ops *
+rte_event_timer_adapter_ops_get(struct rte_event_timer_adapter *adapter)
+{
+ struct rte_eventdev *evdev = &rte_eventdevs[adapter->event_dev_id];
+ struct rte_event_timer_adapter_ops *ops;
+ RTE_SET_USED(evdev);
+
+ if (unlikely(!evdev->dev_ops->event_timer_adapter_ctrl ||
+ evdev->dev_ops->event_timer_adapter_ctrl(&ops) || !ops))
+ return NULL;
+
+ return ops;
+}
+
+int
+rte_event_timer_arm_burst(uint16_t id,
+ struct rte_event_timer **tim,
+ uint16_t nb_tims)
+{
+ const struct rte_event_timer_adapter_ops *ops = NULL;
+ struct rte_event_timer_adapter *adapter;
+
+ ADAPTER_VALID_GET_OR_ERR_RET(id, adapter, -EINVAL);
+
+ ops = rte_event_timer_adapter_ops_get(adapter);
+ if (unlikely(!ops))
+ return -1;
+ if (likely(!!ops->event_timer_arm_burst))
+ return ops->event_timer_arm_burst(adapter, tim, nb_tims);
+
+ return -1;
+}
+
+int
+rte_event_timer_arm_tmo_tick_burst(uint16_t id,
+ struct rte_event_timer **tim,
+ const uint64_t timeout_tick,
+ uint16_t nb_tims)
+{
+ const struct rte_event_timer_adapter_ops *ops = NULL;
+ struct rte_event_timer_adapter *adapter;
+
+ ADAPTER_VALID_GET_OR_ERR_RET(id, adapter, -EINVAL);
+
+ ops = rte_event_timer_adapter_ops_get(adapter);
+ if (unlikely(!ops))
+ return -1;
+ if (likely(!!ops->event_timer_arm_tmo_tick_burst))
+ return ops->event_timer_arm_tmo_tick_burst(adapter,
+ tim,
+ timeout_tick,
+ nb_tims);
+
+ return -1;
+}
+
+int rte_event_timer_cancel_burst(uint16_t id,
+ struct rte_event_timer **tim,
+ uint16_t nb_tims)
+{
+ const struct rte_event_timer_adapter_ops *ops = NULL;
+ struct rte_event_timer_adapter *adapter;
+
+ ADAPTER_VALID_GET_OR_ERR_RET(id, adapter, -EINVAL);
+
+ ops = rte_event_timer_adapter_ops_get(adapter);
+ if (unlikely(!ops))
+ return -1;
+ if (likely(!!ops->event_timer_cancel_burst))
+ return ops->event_timer_cancel_burst(adapter, tim, nb_tims);
+
+ return -1;
+}
diff --git a/lib/librte_eventdev/rte_event_timer_adapter.h b/lib/librte_eventdev/rte_event_timer_adapter.h
new file mode 100644
index 0000000..85ae7da
--- /dev/null
+++ b/lib/librte_eventdev/rte_event_timer_adapter.h
@@ -0,0 +1,388 @@
+/*
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _RTE_EVENT_TIMER_ADAPTER_H_
+#define _RTE_EVENT_TIMER_ADAPTER_H_
+
+/**
+ * @file
+ *
+ * RTE Event Timer Adapter
+ *
+ * TODO: Update description
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "rte_eventdev.h"
+
+/*
+ * Timer adapter clock source
+ */
+enum rte_event_timer_adapter_clk_src {
+ RTE_EVENT_TIMER_ADAPTER_CPU_CLK,
+ /**< Use CPU clock as the clock source. */
+ RTE_EVENT_TIMER_ADAPTER_EXT_CLK0,
+ /**< Platform dependent external clock source 0. */
+ RTE_EVENT_TIMER_ADAPTER_EXT_CLK1,
+ /**< Platform dependent external clock source 1. */
+ RTE_EVENT_TIMER_ADAPTER_EXT_CLK2,
+ /**< Platform dependent external clock source 2. */
+ RTE_EVENT_TIMER_ADAPTER_EXT_CLK3,
+ /**< Platform dependent external clock source 3. */
+};
+
+#define RTE_EVENT_TIMER_ADAPTER_F_ADJUST_RES (1ULL << 0)
+/**< The event timer adapter implementation may have constraints on the
+ * resolution (timer_tick_ns) and maximum timer expiry timeout(max_tmo_ns)
+ * based on the given timer adapter or system. If this flag is set, the
+ * implementation adjusts the resolution and maximum timeout to the best
+ * possible configuration. On successful timer adapter creation, the
+ * application can get the configured resolution and max timeout with
+ * ``rte_event_timer_adapter_get_info()``.
+ */
+#define RTE_EVENT_TIMER_ADAPTER_F_SP_PUT (1ULL << 1)
+/**< ``rte_event_timer_arm_burst()`` API to be used in single producer mode.
+ *
+ * @see struct rte_event_timer_adapter_conf::timer_wheel_flags
+ */
+
+
+/*
+ * Timer adapter configuration structure
+ */
+struct rte_event_timer_adapter_conf {
+ uint8_t event_dev_id;
+ /**< Event device identifier */
+ uint16_t timer_adapter_id;
+ /**< Event timer adapter identifier */
+ enum rte_event_timer_adapter_clk_src clk_src;
+ /**< Clock source for timer adapter */
+ uint64_t timer_tick_ns;
+ /**< Timer wheel resolution in ns */
+ uint64_t max_tmo_ns;
+ /**< Maximum timer timeout(expiry) in ns */
+ uint64_t nb_timers;
+ /**< Total number of timers per adapter */
+ uint32_t timer_adapter_flags;
+ /**< Timer adapter config flags (RTE_EVENT_TIMER_ADAPTER_F_*) */
+};
+
+struct rte_event_timer_adapter; /* opaque pointer */
+
+/*
+ * Callback function type for producer port creation.
+ */
+typedef int (*rte_event_timer_adapter_port_conf_cb_t)(uint16_t id,
+ uint8_t event_dev_id,
+ uint8_t event_port_id,
+ void *conf_arg);
+
+/*
+ * Create an event timer adapter.
+ *
+ * This function must be invoked first before any other function in the API.
+ *
+ * @param conf
+ * The event timer adapter configuration structure.
+ *
+ * @return
+ * Possible rte_errno values include:
+ * - ERANGE: timer_tick_ns is not in supported range.
+ */
+int rte_event_timer_adapter_create(struct rte_event_timer_adapter_conf *conf);
+
+/*
+ * Create a timer adapter with the supplied callback.
+ *
+ * This function can be used to have a more granular control over the timer
+ * adapter creation. If a built-in port is absent, then the function uses the
+ * callback provided to create and get the port id to be used as a producer
+ * port.
+ *
+ * @param conf
+ * The timer adapter configuration structure
+ * @param conf_cb
+ * The port config callback function.
+ * @param conf_arg
+ * Opaque pointer to the argument for the callback function
+ *
+ * @return
+ * Pointer to the new allocated event timer adapter on success.
+ * NULL on error with rte_errno set appropriately.
+ * Possible rte_errno values include:
+ * - ERANGE: timer_tick_ns is not in supported range.
+ */
+int rte_event_timer_adapter_create_ext(
+ struct rte_event_timer_adapter_conf *conf,
+ rte_event_timer_adapter_port_conf_cb_t conf_cb,
+ void *conf_arg);
+
+/*
+ * Timer adapter info structure.
+ */
+struct rte_event_timer_adapter_info {
+ uint64_t min_resolution_ns;
+ /**< Minimum timer adapter resolution in ns */
+ uint64_t max_tmo_ns;
+ /**< Maximum timer timeout(expire) in ns */
+ struct rte_event_timer_adapter_conf conf;
+ /**< Configured timer adapter attributes */
+ uint8_t event_dev_port_id;
+ /* TODO: document this */
+};
+
+/**
+ * Retrieve the contextual information of an event timer adapter.
+ *
+ * @param id
+ * The identifier of an event timer adapter.
+ *
+ * @param[out] adapter_info
+ * A pointer to a structure of type *rte_event_timer_adapter_info* to be
+ * filled with the contextual information of the adapter.
+ *
+ * @return
+ * - 0: Success, driver updates the contextual information of the
+ * timer adapter
+ * - <0: Error code returned by the driver info get function.
+ * - -EINVAL if adapter identifier invalid
+ *
+ * @see RTE_EVENT_TIMER_ADAPTER_F_ADJUST_RES,
+ * struct rte_event_timer_adapter_info
+ *
+ */
+int rte_event_timer_adapter_get_info(uint16_t id,
+ struct rte_event_timer_adapter_info *adapter_info);
+
+/**
+ * Start a timer adapter.
+ *
+ * The adapter start step is the last one and consists of setting the timer
+ * adapter to start accepting the timers and schedules to event queues.
+ *
+ * On success, all basic functions exported by the API (timer arm,
+ * timer cancel and so on) can be invoked.
+ *
+ * @param id
+ * The identifier of an event timer adapter.
+ *
+ * @return
+ * - 0: Success, adapter started.
+ * - <0: Error code returned by the driver start function.
+ * - -EINVAL if adapter identifier invalid
+ */
+int rte_event_timer_adapter_start(uint16_t id);
+
+/**
+ * Stop an event timer adapter.
+ *
+ * The adapter can be restarted with a call to
+ * ``rte_event_timer_adapter_start()``.
+ *
+ * @param id
+ * The identifier of an event timer adapter.
+ *
+ * @return
+ * - 0: Success, adapter stop.
+ * - <0: Error code returned by the driver stop function.
+ * - -EINVAL if adapter identifier invalid
+ */
+int rte_event_timer_adapter_stop(uint16_t id);
+
+/**
+ * Free an event timer adapter.
+ *
+ * Destroy an event timer adapter, freeing all resources.
+ *
+ * Before invoking this function, the application must wait for all the armed
+ * timers to expire or cancel the outstanding armed timers.
+ *
+ * @param id
+ * The identifier of an event timer adapter.
+ *
+ * @return
+ * - 0 on successfully freed the event timer adapter resources.
+ * - <0 on failure to free an event timer adapter.
+ * - -EAGAIN if adapter is busy
+ * - -EINVAL if adapter identifier invalid
+ */
+int rte_event_timer_adapter_free(uint16_t id);
+
+/**
+ * Event timer state.
+ */
+enum rte_event_timer_state {
+ RTE_EVENT_TIMER_NOT_ARMED = 0,
+ /**< Event timer is in not armed state.*/
+ RTE_EVENT_TIMER_ARMED = 1,
+ /**< Event timer successfully armed.*/
+ RTE_EVENT_TIMER_ERROR = -1,
+ /**< Generic event timer error.*/
+ RTE_EVENT_TIMER_ERROR_TOOEARLY = -2,
+ /**< Event timer timeout tick is too little to add to the adapter. */
+ RTE_EVENT_TIMER_ERROR_TOOLATE = -3,
+ /**< Event timer timeout tick is greater than the maximum timeout.*/
+};
+
+/**
+ * The generic *rte_event_timer* structure to hold the event timer attributes
+ * for arm and cancel operations.
+ */
+RTE_STD_C11
+struct rte_event_timer {
+ struct rte_event ev;
+ /**<
+ * Expiry event attributes. On successful event timer timeout,
+ * the following attributes will be used to inject the expiry event to
+ * the eventdev:
+ * - event_queue_id: Targeted event queue id for expiry events.
+ * - event_priority: Event priority of the event expiry event in the
+ * event queue relative to other events.
+ * - sched_type: Scheduling type of the expiry event.
+ * - flow_id: Flow id of the expiry event.
+ * - op: RTE_EVENT_OP_NEW
+ * - event_type: RTE_EVENT_TYPE_TIMER
+ */
+ enum rte_event_timer_state state;
+ /**< State of the event timer. */
+ uint64_t timeout_ticks;
+ /**< Expiry timer ticks expressed in number of *timer_ticks_ns* from
+ * now.
+ * @see struct rte_event_timer_adapter_info::adapter_conf::timer_tick_ns
+ */
+ uint64_t impl_opaque[2];
+ /**< Implementation-specific opaque data.
+ * An event timer adapter implementation use this field to hold
+ * implementation specific values to share between the arm and cancel
+ * operations. The application should not modify this field.
+ */
+ uint8_t user_meta[];
+ /**< Memory to store user specific metadata.
+ * The event timer adapter implementation should not modify this area.
+ */
+} __rte_cache_aligned;
+
+/**
+ * Arm a burst of event timers with separate expiration timeout tick for each
+ * event timer.
+ *
+ * Before calling this function, the application allocates
+ * ``struct rte_event_timer`` objects from mempool or huge page backed
+ * application buffers of desired size. On successful allocation,
+ * application updates the `struct rte_event_timer`` attributes such as
+ * expiry event attributes, timeout ticks from now.
+ * This function submits the event timer arm requests to the event timer adapter
+ * and on expiry, the events will be injected to designated event queue.
+ *
+ * @param id
+ * The identifier of an event timer adapter.
+ * @param tim
+ * Points to an array of objects of type *rte_event_timer* structure.
+ * @param nb_timers
+ * Number of event timers in the supplied array.
+ *
+ * @return
+ * The number of successfully armed event timers. The return value can be less
+ * than the value of the *nb_timers* parameter. If the return value is less
+ * than *nb_events*, the remaining event timers at the end of *tim*
+ * are not consumed, and the caller has to take care of them, and rte_errno
+ * is set accordingly. Possible errno values include:
+ * - -EINVAL Invalid timer adapter identifier, expiry event queue ID is
+ * invalid, or an expiry event's sched type doesn't match the capabilities of
+ * the destination event queue.
+ */
+int rte_event_timer_arm_burst(uint16_t id,
+ struct rte_event_timer **tim,
+ uint16_t nb_timers);
+
+/**
+ * Arm a burst of event timers with same expiration timeout tick.
+ *
+ * Provides the same functionality as ``rte_event_timer_arm_burst()``, except
+ * that application can use this API when all the event timers have the
+ * same timeout expiration tick. This specialized function can provide the
+ * additional hint to the adapter implementation and optimize if possible.
+ *
+ * @param id
+ * The identifier of an event timer adapter.
+ * @param tim
+ * Points to an array of objects of type *rte_event_timer* structure.
+ * @param nb_timers
+ * Number of event timers in the supplied array.
+ *
+ * @return
+ * The number of successfully armed event timers. The return value can be less
+ * than the value of the *nb_timers* parameter. If the return value is less
+ * than *nb_events*, the remaining event timers at the end of *tim*
+ * are not consumed, and the caller has to take care of them, and rte_errno
+ * is set accordingly. Possible errno values include:
+ * - -EINVAL Invalid timer adapter identifier, expiry event queue ID is
+ * invalid, or an expiry event's sched type doesn't match the capabilities of
+ * the destination event queue.
+ */
+int
+rte_event_timer_arm_tmo_tick_burst(
+ uint16_t id,
+ struct rte_event_timer **tim,
+ const uint64_t timeout_tick,
+ const uint16_t nb_timers);
+
+/**
+ * Cancel a burst of event timer from being scheduled to the event device.
+ *
+ * @param id
+ * The identifier of an event timer adapter.
+ * @param tim
+ * Points to an array of objects of type *rte_event_timer* structure
+ * @param nb_timers
+ * Number of event timer instances in the supplied array.
+ *
+ * @return
+ * The number of successfully canceled event timers. The return value can be
+ * less than the value of the *nb_timers* parameter. If the return value is
+ * less than *nb_events*, the remaining event timers at the end of *tim*
+ * are not consumed, and the caller has to take care of them, and rte_errno
+ * is set accordingly. Possible errno values include:
+ * - -EINVAL Invalid timer adapter identifier
+ */
+int rte_event_timer_cancel_burst(uint16_t id,
+ struct rte_event_timer **tim,
+ uint16_t nb_timers);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_EVENT_TIMER_ADAPTER_H_ */
diff --git a/lib/librte_eventdev/rte_event_timer_adapter_driver.h b/lib/librte_eventdev/rte_event_timer_adapter_driver.h
new file mode 100644
index 0000000..d82fe38
--- /dev/null
+++ b/lib/librte_eventdev/rte_event_timer_adapter_driver.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RTE_EVENT_TIMER_ADAPTER_DRIVER_H_
+#define RTE_EVENT_TIMER_ADAPTER_DRIVER_H_
+
+/**
+ * @file
+ * RTE event timer adapter driver side API
+ *
+ * This file provides implementation helpers for internal use by plugins, they
+ * are not intended to be exposed to applications and are not subject to ABI
+ * versioning.
+ */
+#include <rte_timer.h>
+#include <rte_ring.h>
+
+#include "rte_event_timer_adapter.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Event timer adapter structure.
+ */
+struct rte_event_timer_adapter {
+ uint8_t event_dev_id;
+ /**< Event device identifier */
+ uint8_t event_port_id;
+ /**< Event device port identifier */
+ uint16_t id;
+ /**< Event timer adapter identifier */
+ rte_event_timer_adapter_port_conf_cb_t conf_cb;
+ /**< Port configuration callback function for event timer adapter. */
+ void *conf_arg;
+ /**< Argument for port configuration callback function. */
+
+ /**
+ * Fields for software implementation
+ */
+ uint32_t service_id;
+ /**< Identifier of service executing timer management logic. */
+ int socket_id;
+ /**< Socket identifier of service. */
+ struct rte_ring event_timer_ring;
+ /**< Ring of messages submitted by application to arm/cancel event
+ * timers.
+ */
+ /* event buffer */
+ /**< Buffered timer events to be enqueued to an event device. */
+
+ /* stats */
+};
+
+/**
+ * These callback functions are not supposed to be used by applications
+ * directly, which must rely on the API defined in rte_event_timer_adapter.h.
+ *
+ * See also rte_event_timer_adapter_ops_get().
+ */
+struct rte_event_timer_adapter_ops {
+ int (*adapter_start)
+ (struct rte_event_timer_adapter *adapter);
+ int (*adapter_stop)
+ (struct rte_event_timer_adapter *adapter);
+ int (*event_timer_arm_burst)
+ (struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ const uint16_t nb_tims);
+ int (*event_timer_arm_tmo_tick_burst)
+ (struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ const uint64_t timeout_tick,
+ const uint16_t nb_tims);
+ int (*event_timer_cancel_burst)
+ (struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ const uint16_t nb_tims);
+};
+
+/**
+ * Get event timer adapter operations structure from an event device.
+ *
+ * @param adapter
+ * Pointer to adapter for which to obtain an operations struct.
+ *
+ * @return
+ * The adapter operation structure associated with the event device, NULL in
+ * case of error, in which case rte_errno is set and the error structure
+ * contains additional details.
+ */
+const struct rte_event_timer_adapter_ops *
+rte_event_timer_adapter_ops_get(struct rte_event_timer_adapter *adapter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* RTE_EVENT_TIMER_ADAPTER_DRIVER_H_ */
diff --git a/lib/librte_eventdev/rte_eventdev.h b/lib/librte_eventdev/rte_eventdev.h
index 128bc52..8357c5e 100644
--- a/lib/librte_eventdev/rte_eventdev.h
+++ b/lib/librte_eventdev/rte_eventdev.h
@@ -865,7 +865,7 @@ rte_event_dev_close(uint8_t dev_id);
/**< The event generated from ethdev subsystem */
#define RTE_EVENT_TYPE_CRYPTODEV 0x1
/**< The event generated from crypodev subsystem */
-#define RTE_EVENT_TYPE_TIMERDEV 0x2
+#define RTE_EVENT_TYPE_TIMER 0x2
/**< The event generated from timerdev subsystem */
#define RTE_EVENT_TYPE_CPU 0x3
/**< The event generated from cpu for pipelining.
diff --git a/lib/librte_eventdev/rte_eventdev_pmd.h b/lib/librte_eventdev/rte_eventdev_pmd.h
index 3d72acf..27bcc77 100644
--- a/lib/librte_eventdev/rte_eventdev_pmd.h
+++ b/lib/librte_eventdev/rte_eventdev_pmd.h
@@ -412,6 +412,18 @@ typedef int (*eventdev_xstats_get_names_t)(const struct rte_eventdev *dev,
unsigned int *ids, unsigned int size);

/**
+ * Timer adapter control function for an event device. Currently used to
+ * fill an ops structure with function pointers for an adapter plugin.
+ *
+ * @param arg
+ * Pointer to an ops structure to fill
+ * @return
+ * 0: Success
+ * <0: Failure
+ */
+typedef int (*event_timer_adapter_ctrl_t)(void *arg);
+
+/**
* Get value of one stats and optionally return its id
*
* @param dev
@@ -468,6 +480,9 @@ struct rte_eventdev_ops {
/**< Get one value by name. */
eventdev_xstats_reset_t xstats_reset;
/**< Reset the statistics values in xstats. */
+
+ event_timer_adapter_ctrl_t event_timer_adapter_ctrl;
+ /**< Perform actions related to an event timer adapter.*/
};

/**
diff --git a/lib/librte_eventdev/rte_eventdev_version.map b/lib/librte_eventdev/rte_eventdev_version.map
index 4c48e5f..5655d92 100644
--- a/lib/librte_eventdev/rte_eventdev_version.map
+++ b/lib/librte_eventdev/rte_eventdev_version.map
@@ -51,3 +51,17 @@ DPDK_17.08 {
rte_event_ring_init;
rte_event_ring_lookup;
} DPDK_17.05;
+
+DPDK_17.11 {
+ global:
+
+ rte_event_timer_adapter_create;
+ rte_event_timer_adapter_create_ext;
+ rte_event_timer_adapter_get_info;
+ rte_event_timer_adapter_start;
+ rte_event_timer_adapter_stop;
+ rte_event_timer_adapter_free;
+ rte_event_timer_arm_burst;
+ rte_event_timer_arm_tmo_tick_burst;
+ rte_event_timer_cancel_burst;
+} DPDK_17.08;
diff --git a/test/test/Makefile b/test/test/Makefile
index 42d9a49..f09dc73 100644
--- a/test/test/Makefile
+++ b/test/test/Makefile
@@ -204,6 +204,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_CRYPTODEV) += test_cryptodev.c
ifeq ($(CONFIG_RTE_LIBRTE_EVENTDEV),y)
SRCS-y += test_eventdev.c
SRCS-y += test_event_ring.c
+SRCS-y += test_event_timer_adapter.c
SRCS-$(CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV) += test_eventdev_sw.c
SRCS-$(CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF) += test_eventdev_octeontx.c
endif
diff --git a/test/test/test_event_timer_adapter.c b/test/test/test_event_timer_adapter.c
new file mode 100644
index 0000000..46ed098
--- /dev/null
+++ b/test/test/test_event_timer_adapter.c
@@ -0,0 +1,183 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include <rte_eventdev.h>
+#include <rte_dev.h>
+#include <rte_event_timer_adapter.h>
+#include <rte_mempool.h>
+#include <rte_errno.h>
+
+#include "test.h"
+
+static int evdev;
+struct rte_event_timer_adapter *g_adapter;
+struct rte_event_timer *g_evtimer;
+struct rte_mempool *g_event_timer_pool;
+
+static inline void
+devconf_set_default_sane_values(struct rte_event_dev_config *dev_conf,
+ struct rte_event_dev_info *info)
+{
+ memset(dev_conf, 0, sizeof(struct rte_event_dev_config));
+ dev_conf->dequeue_timeout_ns = info->min_dequeue_timeout_ns;
+ dev_conf->nb_event_ports = info->max_event_ports;
+ dev_conf->nb_event_queues = info->max_event_queues;
+ dev_conf->nb_event_queue_flows = info->max_event_queue_flows;
+ dev_conf->nb_event_port_dequeue_depth =
+ info->max_event_port_dequeue_depth;
+ dev_conf->nb_event_port_enqueue_depth =
+ info->max_event_port_enqueue_depth;
+ dev_conf->nb_event_port_enqueue_depth =
+ info->max_event_port_enqueue_depth;
+ dev_conf->nb_events_limit =
+ info->max_num_events;
+}
+
+static int
+configure_event_dev(void)
+{
+ struct rte_event_dev_config devconf;
+ int ret;
+ const char *eventdev_name = "event_sw0";
+ struct rte_event_dev_info info;
+
+ evdev = rte_event_dev_get_dev_id(eventdev_name);
+ if (evdev < 0) {
+ if (rte_vdev_init(eventdev_name, NULL) < 0) {
+ printf("Error creating eventdev\n");
+ return TEST_FAILED;
+ }
+ evdev = rte_event_dev_get_dev_id(eventdev_name);
+ if (evdev < 0) {
+ printf("Error finding newly created eventdev\n");
+ return TEST_FAILED;
+ }
+ }
+
+ ret = rte_event_dev_info_get(evdev, &info);
+ TEST_ASSERT_SUCCESS(ret, "Failed to get event dev info");
+
+ devconf_set_default_sane_values(&devconf, &info);
+
+ ret = rte_event_dev_configure(evdev, &devconf);
+ TEST_ASSERT_SUCCESS(ret, "Failed to configure eventdev");
+
+ /* Start the event device */
+ ret = rte_event_dev_start(evdev);
+ TEST_ASSERT_SUCCESS(ret, "Failed to start device");
+
+ return TEST_SUCCESS;
+}
+
+static int
+testsuite_setup(void)
+{
+ int ret;
+
+ /* Setup and start event device. */
+ ret = configure_event_dev();
+ if (ret) {
+ printf("Failed to configure event dev\n");
+ return TEST_FAILED;
+ }
+
+ /* Create a mempool of event timers. */
+ g_event_timer_pool = rte_mempool_create("event_timer_mempool",
+ 32,
+ sizeof(struct rte_event_timer),
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ rte_socket_id(),
+ 0);
+ if (g_event_timer_pool == NULL) {
+ /* Failed to create event timer mempool. */
+ printf("Failed to configure event timer mempool: %s\n",
+ rte_strerror(rte_errno));
+ return TEST_FAILED;
+ }
+
+ return TEST_SUCCESS;
+}
+
+static void
+testsuite_teardown(void)
+{
+ /* TODO: tear down adapter and evdev */
+
+ rte_mempool_free(g_event_timer_pool);
+}
+
+static int
+adapter_create_free(void)
+{
+ int ret;
+
+ struct rte_event_timer_adapter_conf conf = {
+ .event_dev_id = evdev,
+ .timer_adapter_id = 0,
+ };
+
+ ret = rte_event_timer_adapter_create(&conf);
+ if (ret)
+ return TEST_FAILED;
+
+ ret = rte_event_timer_adapter_free(conf.timer_adapter_id);
+ if (ret)
+ return TEST_FAILED;
+
+ return TEST_SUCCESS;
+}
+
+static struct unit_test_suite adapter_tests = {
+ .suite_name = "event timer adapter test suite",
+ .setup = testsuite_setup,
+ .teardown = testsuite_teardown,
+ .unit_test_cases = {
+ TEST_CASE(adapter_create_free),
+ TEST_CASES_END() /**< NULL terminate unit test array */
+ }
+};
+
+static int
+test_event_timer_adapter_common(void)
+{
+ return unit_test_suite_runner(&adapter_tests);
+}
+
+REGISTER_TEST_COMMAND(event_timer_adapter_autotest,
+ test_event_timer_adapter_common);
--
2.6.4
Jerin Jacob
2017-10-03 14:37:24 UTC
Permalink
-----Original Message-----
Date: Fri, 22 Sep 2017 10:17:10 -0500
Subject: [RFC PATCH v2 0/1] eventtimer: introduce event timer adapter
X-Mailer: git-send-email 1.7.10
Hi Jerin, et al.,
The following RFC patch contains enough of a skeleton implementation
for an event timer adapter to show how the API could call through an ops
structure to a plugin backend to provide different implementations.
In terms of differences from the original RFC, the most obvious one is
that I've replaced occurrences of "wheel" in the API with "adapter",
partly to reflect the similarity with the event_eth_rx_adapter, and
partly because "wheel" suggests an implementation that may not be
the one used (as in the case of the SW impl).
OK. Makes sense. We thought timer wheel is generic concept. Anyway
the name change is fine.
The second big change is to replace API parameters specifying pointer
to rte_event_timer_adapter with ids, which seems more common throughout
DPDK.
- removed rte_event_timer_adapter_lookup() function, since APIs no longer
accept pointer to rte_event_timer_adapter
There is one difference between ethdev rx adapter, where we have
rte_event_timer_arm_burst(), rte_event_timer_arm_tmo_tick_burst(),
rte_event_timer_cancel_burst(),
APIs in fastpath. So any multi process scheme where resolving the
fastpath API functions in _one_ or zero redirection is fine.

I guess in we may need 2 or 3 indirection to resolve the fastpath functions
with id scheme. Please choose scheme with one 1 or no redirection.
I think,
- By allocating adapter memory from the heap and
- adapter hold the function pointers for multi process and
- mempool kind of pointer object scheme without id and lookup()
Can resolve function pointers without any indirection.

So please analyze on those lines as well.
- Replaced RTE_EVENT_TIMER_SUCCESS_{ARM,CANCEL} states with
RTE_EVENT_TIMER_ARMED, since the purpose of the SUCCESS_CANCEL state was
unclear
ARM- to denote it has been armed
CANCEL to denote it has been canceled, ie. on rte_event_timer_cancel_burst()
function it can update the state as CANCELED. So that application can
know the exact status of timer event.
Please have a look and let me know what you think.
Looks like you have started with implementation without commenting on
initial RFC, so I think, you can continue with implementation of common code.
We will contribute on review and adding HW drivers.

Jerin
Thanks,
Gabriel
eventtimer: introduce event timer adapter
Carrillo, Erik G
2017-10-09 20:30:35 UTC
Permalink
Post by Erik Gabriel Carrillo
Post by Erik Gabriel Carrillo
The second big change is to replace API parameters specifying pointer
to rte_event_timer_adapter with ids, which seems more common
throughout DPDK.
- removed rte_event_timer_adapter_lookup() function, since APIs no
longer
Post by Erik Gabriel Carrillo
accept pointer to rte_event_timer_adapter
There is one difference between ethdev rx adapter, where we have
rte_event_timer_arm_burst(), rte_event_timer_arm_tmo_tick_burst(),
rte_event_timer_cancel_burst(),
APIs in fastpath. So any multi process scheme where resolving the fastpath
API functions in _one_ or zero redirection is fine.
I see, makes sense.
Post by Erik Gabriel Carrillo
I guess in we may need 2 or 3 indirection to resolve the fastpath functions
with id scheme. Please choose scheme with one 1 or no redirection.
I think,
- By allocating adapter memory from the heap and
Just to check, are you talking about the heap (e.g., rte_malloc) in hugepage memory?
Post by Erik Gabriel Carrillo
- adapter hold the function pointers for multi process and
- mempool kind of pointer object scheme without id and lookup() Can
resolve function pointers without any indirection.
Can you elaborate here? In the mempool implementation, it looks like there's a per-process array of ops objects, and each mempool instance has an index into the array that it uses to select the ops object to call through. Since the ops structures are initialized per-process, they get function pointers valid in that process - which relates to the second point above. If the adapter holds function pointers for a primary process (for example), they'll be invalid in the secondary. Am I missing something?

Thanks,
Gabriel
Post by Erik Gabriel Carrillo
So please analyze on those lines as well.
Pavan Nikhilesh Bhagavatula
2017-10-16 12:04:37 UTC
Permalink
Post by Carrillo, Erik G
Post by Erik Gabriel Carrillo
Post by Erik Gabriel Carrillo
The second big change is to replace API parameters specifying pointer
to rte_event_timer_adapter with ids, which seems more common
throughout DPDK.
- removed rte_event_timer_adapter_lookup() function, since APIs no
longer
Post by Erik Gabriel Carrillo
accept pointer to rte_event_timer_adapter
There is one difference between ethdev rx adapter, where we have
rte_event_timer_arm_burst(), rte_event_timer_arm_tmo_tick_burst(),
rte_event_timer_cancel_burst(),
APIs in fastpath. So any multi process scheme where resolving the fastpath
API functions in _one_ or zero redirection is fine.
I see, makes sense.
Post by Erik Gabriel Carrillo
I guess in we may need 2 or 3 indirection to resolve the fastpath functions
with id scheme. Please choose scheme with one 1 or no redirection.
I think,
- By allocating adapter memory from the heap and
Just to check, are you talking about the heap (e.g., rte_malloc) in hugepage memory?
Yes, If we use rte_memzone_reserve and create the adapter in the primary
process, the name can be used to do a lookup in the secondary.
Post by Carrillo, Erik G
Post by Erik Gabriel Carrillo
- adapter hold the function pointers for multi process and
- mempool kind of pointer object scheme without id and lookup() Can
resolve function pointers without any indirection.
Can you elaborate here? In the mempool implementation, it looks like there's a per-process array of ops objects, and each mempool instance has an index into the array that it uses to select the ops object to call through. Since the ops structures are initialized per-process, they get function pointers valid in that process - which relates to the second point above. If the adapter holds function pointers for a primary process (for example), they'll be invalid in the secondary. Am I missing something?
Once adapter is created using memzone in the Primary process, the secondary
process can use the memzone name to get the pointer to the adapter and use data
such as ops_id stored in the memzone to fixup the fastpath function pointers.
The slowpath could still use ops_id based function pointers(mempool).
Post by Carrillo, Erik G
Thanks,
Gabriel
Post by Erik Gabriel Carrillo
So please analyze on those lines as well.
Pavan
Pavan Nikhilesh Bhagavatula
2017-10-16 12:37:09 UTC
Permalink
Post by Pavan Nikhilesh Bhagavatula
Post by Carrillo, Erik G
Post by Erik Gabriel Carrillo
Post by Erik Gabriel Carrillo
The second big change is to replace API parameters specifying pointer
to rte_event_timer_adapter with ids, which seems more common
throughout DPDK.
- removed rte_event_timer_adapter_lookup() function, since APIs no
longer
Post by Erik Gabriel Carrillo
accept pointer to rte_event_timer_adapter
There is one difference between ethdev rx adapter, where we have
rte_event_timer_arm_burst(), rte_event_timer_arm_tmo_tick_burst(),
rte_event_timer_cancel_burst(),
APIs in fastpath. So any multi process scheme where resolving the fastpath
API functions in _one_ or zero redirection is fine.
I see, makes sense.
Post by Erik Gabriel Carrillo
I guess in we may need 2 or 3 indirection to resolve the fastpath functions
with id scheme. Please choose scheme with one 1 or no redirection.
I think,
- By allocating adapter memory from the heap and
Just to check, are you talking about the heap (e.g., rte_malloc) in hugepage memory?
Yes, If we use rte_memzone_reserve and create the adapter in the primary
process, the name can be used to do a lookup in the secondary.
Post by Carrillo, Erik G
Post by Erik Gabriel Carrillo
- adapter hold the function pointers for multi process and
- mempool kind of pointer object scheme without id and lookup() Can
resolve function pointers without any indirection.
Can you elaborate here? In the mempool implementation, it looks like there's a per-process array of ops objects, and each mempool instance has an index into the array that it uses to select the ops object to call through. Since the ops structures are initialized per-process, they get function pointers valid in that process - which relates to the second point above. If the adapter holds function pointers for a primary process (for example), they'll be invalid in the secondary. Am I missing something?
Once adapter is created using memzone in the Primary process, the secondary
process can use the memzone name to get the pointer to the adapter and use data
such as ops_id stored in the memzone to fixup the fastpath function pointers.
The slowpath could still use ops_id based function pointers(mempool).
Post by Carrillo, Erik G
Thanks,
Gabriel
Post by Erik Gabriel Carrillo
So please analyze on those lines as well.
Pavan
Adding to this

Have a two level scheme such as:

--Allocate this structure from a global array based on the adapter ID:

struct rte_event_timer_wheel {
event_timer_arm_burst arm_burst;
/**< Pointer to driver arm_burst function. */
event_timer_arm_tmo_tick_burst arm_tmo_tick_burst;
/**< Pointer to driver arm_tmo_tick_burst function. */
event_timer_cancel_burst cancel_burst;
/**< Pointer to driver cancel function. */

--Allocate the data section using memzone reserve.
struct rte_event_timer_wheel_data *data;


/**< Pointer to timer wheel data*/
struct rte_event_timer_wheel_ops *ops;
/**< Driver specific ops */
} __rte_cache_aligned;

/**
* Timer wheel data structure.
*/
struct rte_event_timer_wheel_data {
void *wheel_priv;
/**< Timer wheel private data*/
uint8_t ops_index;
/**< Timer wheel ops index id. */
uint8_t wheel_id;
/**< Timer wheel id. */
uint8_t event_port_id;
/**< Optional Event port used when the inbuilt port is absent.*/
const struct rte_memzone *mz;
/**< Timer wheel memzone pointer. */
struct rte_event_timer_wheel_config conf;
/**< Configuration used to configure the wheel. */

RTE_STD_C11
uint8_t started : 1;
/**< Flag to indicate wheel started. */
} __rte_cache_aligned;

/* Primary process */
*_create(wheel_id, ...)
{
struct rte_event_timer_wheel *wl;

/* do checks */
...

wl = &rte_event_timer_wheels[wheel_id];

...
/* Get ops ID based on eventdev */
...
ret = rte_event_dev_get_timer_wheel_capability(event_dev_id, &flags,
&ops_id);

mz = rte_memzone_reserve(name, ...);
...
/* fill in data */
data = mz->addr;
...

data = ops_id;
...
/* Call the driver specific functions. */
}

/*Secondaty process*/
struct rte_event_timer_wheel *
rte_event_timer_wheel_lookup(uint16_t wheel_id)
{
...
struct rte_event_timer_wheel *wl;
struct rte_event_timer_wheel_data *data;
...

/* Do checks*/
...
/* Look up memzone created in primary. */
mz = rte_memzone_lookup(name);

data = mz->addr;

/* Setup up global array in secondary */
wl = &rte_event_timer_wheels[data->wheel_id];
wl->data = data;
...
/* Use the ops_id set in primary to get the ops in secondary*/
wl->ops = rte_event_timer_wheel_get_ops(data->ops_index);

/* Reset the ops*/
(*wl->ops->set_ops)(wl);

return wl;
}

Hope this clears few things up.
Carrillo, Erik G
2017-10-18 21:48:04 UTC
Permalink
-----Original Message-----
From: Pavan Nikhilesh Bhagavatula
Sent: Monday, October 16, 2017 7:37 AM
Subject: Re: [RFC PATCH v2 0/1] eventtimer: introduce event timer adapter
Post by Pavan Nikhilesh Bhagavatula
Post by Carrillo, Erik G
Post by Erik Gabriel Carrillo
Post by Erik Gabriel Carrillo
The second big change is to replace API parameters specifying
pointer to rte_event_timer_adapter with ids, which seems more
common throughout DPDK.
- removed rte_event_timer_adapter_lookup() function, since APIs no
longer
Post by Erik Gabriel Carrillo
accept pointer to rte_event_timer_adapter
There is one difference between ethdev rx adapter, where we have
rte_event_timer_arm_burst(),
rte_event_timer_arm_tmo_tick_burst(),
Post by Pavan Nikhilesh Bhagavatula
Post by Carrillo, Erik G
Post by Erik Gabriel Carrillo
rte_event_timer_cancel_burst(),
APIs in fastpath. So any multi process scheme where resolving the
fastpath API functions in _one_ or zero redirection is fine.
I see, makes sense.
Post by Erik Gabriel Carrillo
I guess in we may need 2 or 3 indirection to resolve the fastpath
functions with id scheme. Please choose scheme with one 1 or no
redirection.
Post by Pavan Nikhilesh Bhagavatula
Post by Carrillo, Erik G
Post by Erik Gabriel Carrillo
I think,
- By allocating adapter memory from the heap and
Just to check, are you talking about the heap (e.g., rte_malloc) in
hugepage memory?
Post by Pavan Nikhilesh Bhagavatula
Yes, If we use rte_memzone_reserve and create the adapter in the
primary process, the name can be used to do a lookup in the secondary.
Post by Carrillo, Erik G
Post by Erik Gabriel Carrillo
- adapter hold the function pointers for multi process and
- mempool kind of pointer object scheme without id and lookup()
Can resolve function pointers without any indirection.
Can you elaborate here? In the mempool implementation, it looks like
there's a per-process array of ops objects, and each mempool instance has
an index into the array that it uses to select the ops object to call through.
Since the ops structures are initialized per-process, they get function
pointers valid in that process - which relates to the second point above. If the
adapter holds function pointers for a primary process (for example), they'll
be invalid in the secondary. Am I missing something?
Post by Pavan Nikhilesh Bhagavatula
Once adapter is created using memzone in the Primary process, the
secondary process can use the memzone name to get the pointer to the
adapter and use data such as ops_id stored in the memzone to fixup the
fastpath function pointers.
Post by Pavan Nikhilesh Bhagavatula
The slowpath could still use ops_id based function pointers(mempool).
Post by Carrillo, Erik G
Thanks,
Gabriel
Post by Erik Gabriel Carrillo
So please analyze on those lines as well.
Pavan
Adding to this
struct rte_event_timer_wheel {
event_timer_arm_burst arm_burst;
/**< Pointer to driver arm_burst function. */
event_timer_arm_tmo_tick_burst arm_tmo_tick_burst;
/**< Pointer to driver arm_tmo_tick_burst function. */
event_timer_cancel_burst cancel_burst;
/**< Pointer to driver cancel function. */
--Allocate the data section using memzone reserve.
struct rte_event_timer_wheel_data *data;
/**< Pointer to timer wheel data*/
struct rte_event_timer_wheel_ops *ops;
/**< Driver specific ops */
} __rte_cache_aligned;
/**
* Timer wheel data structure.
*/
struct rte_event_timer_wheel_data {
void *wheel_priv;
/**< Timer wheel private data*/
uint8_t ops_index;
/**< Timer wheel ops index id. */
uint8_t wheel_id;
/**< Timer wheel id. */
uint8_t event_port_id;
/**< Optional Event port used when the inbuilt port is absent.*/
const struct rte_memzone *mz;
/**< Timer wheel memzone pointer. */
struct rte_event_timer_wheel_config conf;
/**< Configuration used to configure the wheel. */
RTE_STD_C11
uint8_t started : 1;
/**< Flag to indicate wheel started. */ } __rte_cache_aligned;
/* Primary process */
*_create(wheel_id, ...)
{
struct rte_event_timer_wheel *wl;
/* do checks */
...
wl = &rte_event_timer_wheels[wheel_id];
...
/* Get ops ID based on eventdev */
...
ret = rte_event_dev_get_timer_wheel_capability(event_dev_id,
&flags,
&ops_id);
mz = rte_memzone_reserve(name, ...);
...
/* fill in data */
data = mz->addr;
...
data = ops_id;
...
/* Call the driver specific functions. */ }
/*Secondaty process*/
struct rte_event_timer_wheel *
rte_event_timer_wheel_lookup(uint16_t wheel_id) {
...
struct rte_event_timer_wheel *wl;
struct rte_event_timer_wheel_data *data;
...
/* Do checks*/
...
/* Look up memzone created in primary. */
mz = rte_memzone_lookup(name);
data = mz->addr;
/* Setup up global array in secondary */
wl = &rte_event_timer_wheels[data->wheel_id];
wl->data = data;
...
/* Use the ops_id set in primary to get the ops in secondary*/
wl->ops = rte_event_timer_wheel_get_ops(data->ops_index);
/* Reset the ops*/
(*wl->ops->set_ops)(wl);
return wl;
}
Hope this clears few things up.
Hi Pavan,

OK, I think I see what you mean. We could have a global table of ops structs (one per plugin), where each entry contained function pointers valid in that process. We could save the ops_index in the shared data portion of the timer wheel, and in create() or lookup(), we could use that index to retrieve the ops struct, which could contain an operation to set the fastpath pointers for the timer wheel instance. I assume the *lookup() function would rely on the memzone having a predictable name (e.g. "timer_wheel_memzone_<id>") so that it could locate it knowing only the wheel_id.

Thanks for the info,
Gabriel
Pavan Nikhilesh Bhagavatula
2017-10-26 15:45:40 UTC
Permalink
Post by Carrillo, Erik G
-----Original Message-----
From: Pavan Nikhilesh Bhagavatula
Sent: Monday, October 16, 2017 7:37 AM
Subject: Re: [RFC PATCH v2 0/1] eventtimer: introduce event timer adapter
Post by Pavan Nikhilesh Bhagavatula
Post by Carrillo, Erik G
Post by Erik Gabriel Carrillo
Post by Erik Gabriel Carrillo
The second big change is to replace API parameters specifying
pointer to rte_event_timer_adapter with ids, which seems more
common throughout DPDK.
- removed rte_event_timer_adapter_lookup() function, since APIs no
longer
Post by Erik Gabriel Carrillo
accept pointer to rte_event_timer_adapter
There is one difference between ethdev rx adapter, where we have
rte_event_timer_arm_burst(),
rte_event_timer_arm_tmo_tick_burst(),
Post by Pavan Nikhilesh Bhagavatula
Post by Carrillo, Erik G
Post by Erik Gabriel Carrillo
rte_event_timer_cancel_burst(),
APIs in fastpath. So any multi process scheme where resolving the
fastpath API functions in _one_ or zero redirection is fine.
I see, makes sense.
Post by Erik Gabriel Carrillo
I guess in we may need 2 or 3 indirection to resolve the fastpath
functions with id scheme. Please choose scheme with one 1 or no
redirection.
Post by Pavan Nikhilesh Bhagavatula
Post by Carrillo, Erik G
Post by Erik Gabriel Carrillo
I think,
- By allocating adapter memory from the heap and
Just to check, are you talking about the heap (e.g., rte_malloc) in
hugepage memory?
Post by Pavan Nikhilesh Bhagavatula
Yes, If we use rte_memzone_reserve and create the adapter in the
primary process, the name can be used to do a lookup in the secondary.
Post by Carrillo, Erik G
Post by Erik Gabriel Carrillo
- adapter hold the function pointers for multi process and
- mempool kind of pointer object scheme without id and lookup()
Can resolve function pointers without any indirection.
Can you elaborate here? In the mempool implementation, it looks like
there's a per-process array of ops objects, and each mempool instance has
an index into the array that it uses to select the ops object to call through.
Since the ops structures are initialized per-process, they get function
pointers valid in that process - which relates to the second point above. If the
adapter holds function pointers for a primary process (for example), they'll
be invalid in the secondary. Am I missing something?
Post by Pavan Nikhilesh Bhagavatula
Once adapter is created using memzone in the Primary process, the
secondary process can use the memzone name to get the pointer to the
adapter and use data such as ops_id stored in the memzone to fixup the
fastpath function pointers.
Post by Pavan Nikhilesh Bhagavatula
The slowpath could still use ops_id based function pointers(mempool).
Post by Carrillo, Erik G
Thanks,
Gabriel
Post by Erik Gabriel Carrillo
So please analyze on those lines as well.
Pavan
Adding to this
struct rte_event_timer_wheel {
event_timer_arm_burst arm_burst;
/**< Pointer to driver arm_burst function. */
event_timer_arm_tmo_tick_burst arm_tmo_tick_burst;
/**< Pointer to driver arm_tmo_tick_burst function. */
event_timer_cancel_burst cancel_burst;
/**< Pointer to driver cancel function. */
--Allocate the data section using memzone reserve.
struct rte_event_timer_wheel_data *data;
/**< Pointer to timer wheel data*/
struct rte_event_timer_wheel_ops *ops;
/**< Driver specific ops */
} __rte_cache_aligned;
/**
* Timer wheel data structure.
*/
struct rte_event_timer_wheel_data {
void *wheel_priv;
/**< Timer wheel private data*/
uint8_t ops_index;
/**< Timer wheel ops index id. */
uint8_t wheel_id;
/**< Timer wheel id. */
uint8_t event_port_id;
/**< Optional Event port used when the inbuilt port is absent.*/
const struct rte_memzone *mz;
/**< Timer wheel memzone pointer. */
struct rte_event_timer_wheel_config conf;
/**< Configuration used to configure the wheel. */
RTE_STD_C11
uint8_t started : 1;
/**< Flag to indicate wheel started. */ } __rte_cache_aligned;
/* Primary process */
*_create(wheel_id, ...)
{
struct rte_event_timer_wheel *wl;
/* do checks */
...
wl = &rte_event_timer_wheels[wheel_id];
...
/* Get ops ID based on eventdev */
...
ret = rte_event_dev_get_timer_wheel_capability(event_dev_id, &flags,
&ops_id);
mz = rte_memzone_reserve(name, ...);
...
/* fill in data */
data = mz->addr;
...
data = ops_id;
...
/* Call the driver specific functions. */ }
/*Secondaty process*/
struct rte_event_timer_wheel *
rte_event_timer_wheel_lookup(uint16_t wheel_id) {
...
struct rte_event_timer_wheel *wl;
struct rte_event_timer_wheel_data *data;
...
/* Do checks*/
...
/* Look up memzone created in primary. */
mz = rte_memzone_lookup(name);
data = mz->addr;
/* Setup up global array in secondary */
wl = &rte_event_timer_wheels[data->wheel_id];
wl->data = data;
...
/* Use the ops_id set in primary to get the ops in secondary*/
wl->ops = rte_event_timer_wheel_get_ops(data->ops_index);
/* Reset the ops*/
(*wl->ops->set_ops)(wl);
return wl;
}
Hope this clears few things up.
Hi Pavan,
OK, I think I see what you mean. We could have a global table of ops structs (one per plugin), where each entry contained function pointers valid in that process. We could save the ops_index in the shared data portion of the timer wheel, and in create() or lookup(), we could use that index to retrieve the ops struct, which could contain an operation to set the fastpath pointers for the timer wheel instance. I assume the *lookup() function would rely on the memzone having a predictable name (e.g. "timer_wheel_memzone_<id>") so that it could locate it knowing only the wheel_id.
Ack
Post by Carrillo, Erik G
Thanks for the info,
Gabriel
Cheers,
Pavan
Erik Gabriel Carrillo
2017-11-20 22:35:32 UTC
Permalink
The following RFC patch contains a reworked version of the event timer adapter
logic that would be common to both the software and hardware implementations.
It contains an allocation scheme that separates the adapter state into two
parts: one part that is local to a process, and another that is shared between
processes. The process-local part contains fast-path function pointers valid
only in that process, and the shared part contains data that can be used in
both processes. It also lays out the mechanism by which eventdev PMDs can
plug their own implementations into the API if they wish to: if a PMD wishes
to provide an implementation for the event timer adapter API, it can define and
return an ops structure which the API will call through. Otherwise, a PMD
can return a null ops structure to allow the API to fall back on a default
software implementation that will be added in an upcoming version of this
patch.

checkpatches.sh reports a few warnings for "macros with flow control", but I
have left them in since they seem to be in common usage in DPDK. One other
checkpatch warning is caused by use of an extern in a .c file, but I left that
in since it was useful for splitting the software implementation into its own
file.

Thanks,
Gabriel

Erik Gabriel Carrillo (1):
eventtimer: introduce event timer adapter

doc/api/doxy-api-index.md | 1 +
drivers/event/sw/sw_evdev.c | 16 +
lib/librte_eventdev/Makefile | 4 +
lib/librte_eventdev/rte_event_timer_adapter.c | 428 +++++++++++++++++++++
lib/librte_eventdev/rte_event_timer_adapter.h | 427 ++++++++++++++++++++
.../rte_event_timer_adapter_driver.h | 159 ++++++++
lib/librte_eventdev/rte_eventdev.h | 3 +
lib/librte_eventdev/rte_eventdev_pmd.h | 7 +
lib/librte_eventdev/rte_eventdev_version.map | 15 +-
lib/librte_eventdev/sw_event_timer_adapter.c | 123 ++++++
test/test/Makefile | 1 +
test/test/test_event_timer_adapter.c | 249 ++++++++++++
12 files changed, 1432 insertions(+), 1 deletion(-)
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter.c
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter.h
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter_driver.h
create mode 100644 lib/librte_eventdev/sw_event_timer_adapter.c
create mode 100644 test/test/test_event_timer_adapter.c
--
2.6.4
Erik Gabriel Carrillo
2017-11-20 22:35:33 UTC
Permalink
Signed-off-by: Erik Gabriel Carrillo <***@intel.com>
---
v3
- Reworked allocation and ops organization in common code based on feedback
received from Jerin and Pavan. This will allow fast-path function pointers to
be dereferenced with one level of indirection with pointers valid in primary
and secondary processes.
- Moved default software implementation from sw_evdev directory to eventdev
library directory, which will allow it to be used by any eventdev PMD as an
alternative to providing its own definitions.
- Reverted occurrences of id back to pointer to adapter struct in library API
- Added rte_event_timer_adapter_lookup() function back in

v2
- Added ops structure and stubbed out plugin for SW impl
- Added unit test stubs
- Replaced occurrences of "wheel" in API with "adapter"
- Replaced occurrences of pointer to struct rte_event_timer_adapter with ids
- Removed rte_event_timer_adapter_lookup() function
- Replaced RTE_EVENT_TIMER_SUCCESS_{ARM,CANCEL} states with
RTE_EVENT_TIMER_ARMED

doc/api/doxy-api-index.md | 1 +
drivers/event/sw/sw_evdev.c | 16 +
lib/librte_eventdev/Makefile | 4 +
lib/librte_eventdev/rte_event_timer_adapter.c | 428 +++++++++++++++++++++
lib/librte_eventdev/rte_event_timer_adapter.h | 427 ++++++++++++++++++++
.../rte_event_timer_adapter_driver.h | 159 ++++++++
lib/librte_eventdev/rte_eventdev.h | 3 +
lib/librte_eventdev/rte_eventdev_pmd.h | 7 +
lib/librte_eventdev/rte_eventdev_version.map | 15 +-
lib/librte_eventdev/sw_event_timer_adapter.c | 123 ++++++
test/test/Makefile | 1 +
test/test/test_event_timer_adapter.c | 249 ++++++++++++
12 files changed, 1432 insertions(+), 1 deletion(-)
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter.c
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter.h
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter_driver.h
create mode 100644 lib/librte_eventdev/sw_event_timer_adapter.c
create mode 100644 test/test/test_event_timer_adapter.c

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 3492702..3110658 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -46,6 +46,7 @@ The public API headers are grouped by topics:
[security] (@ref rte_security.h),
[eventdev] (@ref rte_eventdev.h),
[event_eth_rx_adapter] (@ref rte_event_eth_rx_adapter.h),
+ [event_timer_adapter] (@ref rte_event_timer_adapter.h),
[metrics] (@ref rte_metrics.h),
[bitrate] (@ref rte_bitrate.h),
[latency] (@ref rte_latencystats.h),
diff --git a/drivers/event/sw/sw_evdev.c b/drivers/event/sw/sw_evdev.c
index fd11079..cba8c8c 100644
--- a/drivers/event/sw/sw_evdev.c
+++ b/drivers/event/sw/sw_evdev.c
@@ -435,6 +435,20 @@ sw_eth_rx_adapter_caps_get(const struct rte_eventdev *dev,
return 0;
}

+static int
+sw_timer_adapter_caps_get(const struct rte_eventdev *dev,
+ uint32_t *caps,
+ const struct rte_event_timer_adapter_ops **ops)
+{
+ RTE_SET_USED(dev);
+ *caps = 0;
+
+ /* Use default SW ops */
+ *ops = NULL;
+
+ return 0;
+}
+
static void
sw_info_get(struct rte_eventdev *dev, struct rte_event_dev_info *info)
{
@@ -755,6 +769,8 @@ sw_probe(struct rte_vdev_device *vdev)

.eth_rx_adapter_caps_get = sw_eth_rx_adapter_caps_get,

+ .timer_adapter_caps_get = sw_timer_adapter_caps_get,
+
.xstats_get = sw_xstats_get,
.xstats_get_names = sw_xstats_get_names,
.xstats_get_by_name = sw_xstats_get_by_name,
diff --git a/lib/librte_eventdev/Makefile b/lib/librte_eventdev/Makefile
index 5ac22cd..d5c7cc7 100644
--- a/lib/librte_eventdev/Makefile
+++ b/lib/librte_eventdev/Makefile
@@ -45,6 +45,8 @@ LDLIBS += -lrte_eal -lrte_ring -lrte_ethdev -lrte_hash
SRCS-y += rte_eventdev.c
SRCS-y += rte_event_ring.c
SRCS-y += rte_event_eth_rx_adapter.c
+SRCS-y += rte_event_timer_adapter.c
+SRCS-y += sw_event_timer_adapter.c

# export include files
SYMLINK-y-include += rte_eventdev.h
@@ -53,6 +55,8 @@ SYMLINK-y-include += rte_eventdev_pmd_pci.h
SYMLINK-y-include += rte_eventdev_pmd_vdev.h
SYMLINK-y-include += rte_event_ring.h
SYMLINK-y-include += rte_event_eth_rx_adapter.h
+SYMLINK-y-include += rte_event_timer_adapter.h
+SYMLINK-y-include += rte_event_timer_adapter_driver.h

# versioning export map
EXPORT_MAP := rte_eventdev_version.map
diff --git a/lib/librte_eventdev/rte_event_timer_adapter.c b/lib/librte_eventdev/rte_event_timer_adapter.c
new file mode 100644
index 0000000..09dcf0f
--- /dev/null
+++ b/lib/librte_eventdev/rte_event_timer_adapter.c
@@ -0,0 +1,428 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include <rte_memzone.h>
+#include <rte_memory.h>
+#include <rte_dev.h>
+#include <rte_errno.h>
+
+#include "rte_eventdev.h"
+#include "rte_eventdev_pmd.h"
+#include "rte_event_timer_adapter.h"
+#include "rte_event_timer_adapter_driver.h"
+
+#define MAX_EVENT_TIMER_ADAPTERS 64
+#define DATA_MZ_NAME_MAX_LEN 64
+#define DATA_MZ_NAME_FORMAT "rte_event_timer_adapter_data_%d"
+
+static struct rte_event_timer_adapter adapters[MAX_EVENT_TIMER_ADAPTERS];
+
+extern const struct rte_event_timer_adapter_ops sw_event_adapter_timer_ops;
+
+static inline int
+adapter_valid(const struct rte_event_timer_adapter *adapter)
+{
+ return adapter != NULL && adapter->allocated == 1;
+}
+
+#define ADAPTER_VALID_OR_ERR_RET(adapter, retval) do { \
+ if (!adapter_valid(adapter)) \
+ return retval; \
+} while (0)
+
+#define FUNC_PTR_OR_ERR_RET(func, errval) do { \
+ if ((func) == NULL) \
+ return errval; \
+} while (0)
+
+#define FUNC_PTR_OR_NULL_RET_WITH_ERRNO(func, errval) do { \
+ if ((func) == NULL) { \
+ rte_errno = errval; \
+ return NULL; \
+ } \
+} while (0)
+
+static int
+default_port_conf_cb(uint16_t id, uint8_t event_dev_id, uint8_t *event_port_id,
+ void *conf_arg)
+{
+ struct rte_event_timer_adapter *adapter;
+ struct rte_eventdev *dev;
+ struct rte_event_dev_config dev_conf;
+ struct rte_event_port_conf *port_conf = conf_arg;
+ int started;
+ uint8_t port_id;
+ uint8_t dev_id;
+ int ret;
+
+ RTE_SET_USED(event_dev_id);
+
+ adapter = &adapters[id];
+ dev = &rte_eventdevs[adapter->data->event_dev_id];
+ dev_id = dev->data->dev_id;
+ dev_conf = dev->data->dev_conf;
+
+ started = dev->data->dev_started;
+ if (started)
+ rte_event_dev_stop(dev_id);
+
+ port_id = dev_conf.nb_event_ports;
+ dev_conf.nb_event_ports += 1;
+ ret = rte_event_dev_configure(dev_id, &dev_conf);
+ if (ret < 0) {
+ if (started)
+ rte_event_dev_start(dev_id);
+
+ return ret;
+ }
+
+ ret = rte_event_port_setup(dev_id, port_id, port_conf);
+ if (ret < 0)
+ return ret;
+
+ *event_port_id = port_id;
+
+ if (started)
+ rte_event_dev_start(dev_id);
+
+ return 0;
+}
+
+struct rte_event_timer_adapter *
+rte_event_timer_adapter_create(const struct rte_event_timer_adapter_conf *conf)
+{
+ /* default port conf values */
+ struct rte_event_port_conf port_conf = {
+ .new_event_threshold = 128,
+ .dequeue_depth = 32,
+ .enqueue_depth = 32
+ };
+
+ return rte_event_timer_adapter_create_ext(conf, default_port_conf_cb,
+ &port_conf);
+}
+
+struct rte_event_timer_adapter *
+rte_event_timer_adapter_create_ext(
+ const struct rte_event_timer_adapter_conf *conf,
+ rte_event_timer_adapter_port_conf_cb_t conf_cb,
+ void *conf_arg)
+{
+ uint16_t adapter_id;
+ struct rte_event_timer_adapter *adapter;
+ const struct rte_memzone *mz;
+ char mz_name[DATA_MZ_NAME_MAX_LEN];
+ int n, ret;
+ struct rte_eventdev *dev;
+
+ if (conf == NULL) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ /* Check eventdev ID */
+ if (!rte_event_pmd_is_valid_dev(conf->event_dev_id)) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+ dev = &rte_eventdevs[conf->event_dev_id];
+
+ adapter_id = conf->timer_adapter_id;
+
+ /* Check adapter ID not already allocated */
+ adapter = &adapters[adapter_id];
+ if (adapter->allocated) {
+ rte_errno = -EEXIST;
+ return NULL;
+ }
+
+ /* Create shared data area. */
+ n = snprintf(mz_name, sizeof(mz_name), DATA_MZ_NAME_FORMAT, adapter_id);
+ if (n >= (int)sizeof(mz_name)) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+ mz = rte_memzone_reserve(mz_name,
+ sizeof(struct rte_event_timer_adapter_data),
+ conf->socket_id, 0);
+ if (mz == NULL)
+ /* rte_errno set by rte_memzone_reserve */
+ return NULL;
+
+ adapter->data = mz->addr;
+ memset(adapter->data, 0, sizeof(struct rte_event_timer_adapter_data));
+
+ adapter->data->mz = mz;
+ adapter->data->event_dev_id = conf->event_dev_id;
+ adapter->data->id = adapter_id;
+ adapter->data->socket_id = conf->socket_id;
+ adapter->data->conf = *conf; /* copy conf structure */
+
+ /* Query eventdev PMD for timer adapter capabilities and ops */
+ ret = dev->dev_ops->timer_adapter_caps_get(dev,
+ &adapter->data->caps,
+ &adapter->ops);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ if (!(adapter->data->caps &
+ RTE_EVENT_TIMER_ADAPTER_CAP_INTERNAL_PORT)) {
+ FUNC_PTR_OR_NULL_RET_WITH_ERRNO(conf_cb, -EINVAL);
+ ret = conf_cb(adapter->data->id, adapter->data->event_dev_id,
+ &adapter->data->event_port_id, conf_arg);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+ }
+
+ /* If eventdev PMD did not provide ops, use default software
+ * implementation.
+ */
+ if (adapter->ops == NULL)
+ adapter->ops = &sw_event_adapter_timer_ops;
+
+ /* Allow driver to do some setup */
+ FUNC_PTR_OR_NULL_RET_WITH_ERRNO(adapter->ops->init, -ENOTSUP);
+ ret = adapter->ops->init(adapter);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ /* Set fast-path function pointers */
+ adapter->arm_burst = adapter->ops->arm_burst;
+ adapter->arm_tmo_tick_burst = adapter->ops->arm_tmo_tick_burst;
+ adapter->cancel_burst = adapter->ops->cancel_burst;
+
+ adapter->allocated = 1;
+
+ return adapter;
+}
+
+int
+rte_event_timer_adapter_get_info(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer_adapter_info *adapter_info)
+{
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+
+ if (adapter->ops->get_info)
+ /* let driver set values it knows */
+ adapter->ops->get_info(adapter, adapter_info);
+
+ /* Set common values */
+ adapter_info->conf = adapter->data->conf;
+ adapter_info->event_dev_port_id = adapter->data->event_port_id;
+ adapter_info->caps = adapter->data->caps;
+
+ return 0;
+}
+
+int
+rte_event_timer_adapter_start(const struct rte_event_timer_adapter *adapter)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->ops->start, -EINVAL);
+
+ ret = adapter->ops->start(adapter);
+ if (ret < 0)
+ return ret;
+
+ adapter->data->started = 1;
+
+ return 0;
+}
+
+int
+rte_event_timer_adapter_stop(const struct rte_event_timer_adapter *adapter)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->ops->stop, -EINVAL);
+
+ ret = adapter->ops->stop(adapter);
+ if (ret < 0)
+ return ret;
+
+ adapter->data->started = 0;
+
+ return 0;
+}
+
+struct rte_event_timer_adapter *
+rte_event_timer_adapter_lookup(uint16_t adapter_id)
+{
+ char name[DATA_MZ_NAME_MAX_LEN];
+ const struct rte_memzone *mz;
+ struct rte_event_timer_adapter_data *data;
+ struct rte_event_timer_adapter *adapter;
+ int ret;
+ struct rte_eventdev *dev;
+
+ if (adapters[adapter_id].allocated)
+ return &adapters[adapter_id]; /* Adapter is already loaded */
+
+ snprintf(name, DATA_MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, adapter_id);
+ mz = rte_memzone_lookup(name);
+ if (mz == NULL) {
+ rte_errno = -ENOENT;
+ return NULL;
+ }
+
+ data = mz->addr;
+
+ adapter = &adapters[data->id];
+ adapter->data = data;
+
+ dev = &rte_eventdevs[adapter->data->event_dev_id];
+
+ /* Query eventdev PMD for timer adapter capabilities and ops */
+ ret = dev->dev_ops->timer_adapter_caps_get(dev,
+ &adapter->data->caps,
+ &adapter->ops);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ /* If eventdev PMD did not provide ops, use default software
+ * implementation.
+ */
+ if (adapter->ops == NULL)
+ adapter->ops = &sw_event_adapter_timer_ops;
+
+ /* Set fast-path function pointers */
+ adapter->arm_burst = adapter->ops->arm_burst;
+ adapter->arm_tmo_tick_burst = adapter->ops->arm_tmo_tick_burst;
+ adapter->cancel_burst = adapter->ops->cancel_burst;
+
+ adapter->allocated = 1;
+
+ return adapter;
+}
+
+int
+rte_event_timer_adapter_free(struct rte_event_timer_adapter *adapter)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->ops->uninit, -EINVAL);
+
+ /* free impl priv data */
+ ret = adapter->ops->uninit(adapter);
+ if (ret < 0)
+ return ret;
+
+ /* free shared data area */
+ ret = rte_memzone_free(adapter->data->mz);
+ if (ret < 0)
+ return ret;
+
+ adapter->data = NULL;
+ adapter->allocated = 0;
+
+ return 0;
+}
+
+int
+rte_event_timer_arm_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ uint16_t nb_event_timers)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->arm_burst, -EINVAL);
+
+ if (!adapter->data->started)
+ return -EAGAIN;
+
+ ret = adapter->arm_burst(adapter, event_timers, nb_event_timers);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int
+rte_event_timer_arm_tmo_tick_burst(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ const uint64_t timeout_ticks,
+ const uint16_t nb_event_timers)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->arm_tmo_tick_burst, -EINVAL);
+
+ if (!adapter->data->started)
+ return -EAGAIN;
+
+ for (int i = 0; i < nb_event_timers; i++)
+ event_timers[i]->timeout_ticks = timeout_ticks;
+
+ ret = adapter->arm_tmo_tick_burst(adapter, event_timers, timeout_ticks,
+ nb_event_timers);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int
+rte_event_timer_cancel_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ uint16_t nb_event_timers)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->cancel_burst, -EINVAL);
+
+ if (!adapter->data->started)
+ return -EAGAIN;
+
+ ret = adapter->cancel_burst(adapter, event_timers, nb_event_timers);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
diff --git a/lib/librte_eventdev/rte_event_timer_adapter.h b/lib/librte_eventdev/rte_event_timer_adapter.h
new file mode 100644
index 0000000..14661b5
--- /dev/null
+++ b/lib/librte_eventdev/rte_event_timer_adapter.h
@@ -0,0 +1,427 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __RTE_EVENT_TIMER_ADAPTER_H__
+#define __RTE_EVENT_TIMER_ADAPTER_H__
+
+/**
+ * @file
+ *
+ * RTE Event Timer Adapter
+ *
+ * TODO: description
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_spinlock.h>
+#include <rte_memory.h>
+
+#include "rte_eventdev.h"
+
+#define RTE_EVENT_TIMER_ADAPTER_NUM_MAX 64
+
+/**
+ * Timer adapter clock source
+ */
+enum rte_event_timer_adapter_clk_src {
+ RTE_EVENT_TIMER_ADAPTER_CPU_CLK,
+ /**< Use CPU clock as the clock source. */
+ RTE_EVENT_TIMER_ADAPTER_EXT_CLK0,
+ /**< Platform dependent external clock source 0. */
+ RTE_EVENT_TIMER_ADAPTER_EXT_CLK1,
+ /**< Platform dependent external clock source 1. */
+ RTE_EVENT_TIMER_ADAPTER_EXT_CLK2,
+ /**< Platform dependent external clock source 2. */
+ RTE_EVENT_TIMER_ADAPTER_EXT_CLK3,
+ /**< Platform dependent external clock source 3. */
+};
+
+#define RTE_EVENT_TIMER_ADAPTER_F_ADJUST_RES (1ULL << 0)
+/**< The event timer adapter implementation may have constraints on the
+ * resolution (timer_tick_ns) and maximum timer expiry timeout(max_tmo_ns)
+ * based on the given timer adapter or system. If this flag is set, the
+ * implementation adjusts the resolution and maximum timeout to the best
+ * possible configuration. On successful timer adapter creation, the
+ * application can get the configured resolution and max timeout with
+ * ``rte_event_timer_adapter_get_info()``.
+ */
+#define RTE_EVENT_TIMER_ADAPTER_F_SP_PUT (1ULL << 1)
+/**< ``rte_event_timer_arm_burst()`` API to be used in single producer mode.
+ *
+ * @see struct rte_event_timer_adapter_conf::flags
+ */
+
+/*
+ * Timer adapter configuration structure
+ */
+struct rte_event_timer_adapter_conf {
+ uint8_t event_dev_id;
+ /**< Event device identifier */
+ uint16_t timer_adapter_id;
+ /**< Event timer adapter identifier */
+ uint32_t socket_id;
+ /**< Identifer of socket from which to allocate memory for adapter */
+ enum rte_event_timer_adapter_clk_src clk_src;
+ /**< Clock source for timer adapter */
+ uint64_t timer_tick_ns;
+ /**< Timer adapter resolution in ns */
+ uint64_t max_tmo_ns;
+ /**< Maximum timer timeout(expiry) in ns */
+ uint64_t nb_timers;
+ /**< Total number of timers per adapter */
+ uint64_t flags;
+ /**< Timer adapter config flags (RTE_EVENT_TIMER_ADAPTER_F_*) */
+};
+
+struct rte_event_timer_adapter;
+
+/*
+ * Callback function type for producer port creation.
+ */
+typedef int (*rte_event_timer_adapter_port_conf_cb_t)(uint16_t id,
+ uint8_t event_dev_id,
+ uint8_t *event_port_id,
+ void *conf_arg);
+
+/*
+ * Create an event timer adapter.
+ *
+ * This function must be invoked first before any other function in the API.
+ *
+ * @param conf
+ * The event timer adapter configuration structure.
+ *
+ * @return
+ * A pointer to the new allocated event timer adapter on success.
+ * NULL on error with rte_errno set appropriately.
+ * Possible rte_errno values include:
+ * - ERANGE: timer_tick_ns is not in supported range.
+ */
+struct rte_event_timer_adapter *rte_event_timer_adapter_create(
+ const struct rte_event_timer_adapter_conf *conf);
+
+/*
+ * Create a timer adapter with the supplied callback.
+ *
+ * This function can be used to have a more granular control over the timer
+ * adapter creation. If a built-in port is absent, then the function uses the
+ * callback provided to create and get the port id to be used as a producer
+ * port.
+ *
+ * @param conf
+ * The timer adapter configuration structure
+ * @param conf_cb
+ * The port config callback function.
+ * @param conf_arg
+ * Opaque pointer to the argument for the callback function
+ * @param id_ptr[out]
+ * Address of variable to store adapter identifier in
+ *
+ * @return
+ * A pointer to the new allocated event timer adapter on success.
+ * NULL on error with rte_errno set appropriately.
+ * Possible rte_errno values include:
+ * - ERANGE: timer_tick_ns is not in supported range.
+ * - ENOMEM: unable to allocate sufficient memory for adapter instances
+ * - EINVAL: invalid event device identifier specified in config
+ * - ENOSPC: maximum number of adapters already created
+ */
+struct rte_event_timer_adapter *rte_event_timer_adapter_create_ext(
+ const struct rte_event_timer_adapter_conf *conf,
+ rte_event_timer_adapter_port_conf_cb_t conf_cb,
+ void *conf_arg);
+
+/*
+ * Timer adapter info structure.
+ */
+struct rte_event_timer_adapter_info {
+ uint64_t min_resolution_ns;
+ /**< Minimum timer adapter resolution in ns */
+ uint64_t max_tmo_ns;
+ /**< Maximum timer timeout(expire) in ns */
+ struct rte_event_timer_adapter_conf conf;
+ /**< Configured timer adapter attributes */
+ uint32_t caps;
+ /**< Event timer adapter capabilities */
+ int16_t event_dev_port_id;
+ /**< Event device port ID, if applicable */
+ int32_t service_id;
+ /**< Service ID, if applicable */
+};
+
+/**
+ * Retrieve the contextual information of an event timer adapter.
+ *
+ * @param adapter
+ * A pointer to the event timer adapter structure.
+ *
+ * @param[out] adapter_info
+ * A pointer to a structure of type *rte_event_timer_adapter_info* to be
+ * filled with the contextual information of the adapter.
+ *
+ * @return
+ * - 0: Success, driver updates the contextual information of the
+ * timer adapter
+ * - <0: Error code returned by the driver info get function.
+ * - -EINVAL if adapter identifier invalid
+ *
+ * @see RTE_EVENT_TIMER_ADAPTER_F_ADJUST_RES,
+ * struct rte_event_timer_adapter_info
+ *
+ */
+int rte_event_timer_adapter_get_info(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer_adapter_info *adapter_info);
+
+/**
+ * Start a timer adapter.
+ *
+ * The adapter start step is the last one and consists of setting the timer
+ * adapter to start accepting the timers and schedules to event queues.
+ *
+ * On success, all basic functions exported by the API (timer arm,
+ * timer cancel and so on) can be invoked.
+ *
+ * @param adapter
+ * A pointer to the event timer adapter structure.
+ *
+ * @return
+ * - 0: Success, adapter started.
+ * - <0: Error code returned by the driver start function.
+ * - -EINVAL if adapter identifier invalid
+ */
+int rte_event_timer_adapter_start(
+ const struct rte_event_timer_adapter *adapter);
+
+/**
+ * Stop an event timer adapter.
+ *
+ * The adapter can be restarted with a call to
+ * ``rte_event_timer_adapter_start()``.
+ *
+ * @param adapter
+ * A pointer to the event timer adapter structure.
+ *
+ * @return
+ * - 0: Success, adapter stopped.
+ * - <0: Error code returned by the driver stop function.
+ * - -EINVAL if adapter identifier invalid
+ */
+int rte_event_timer_adapter_stop(const struct rte_event_timer_adapter *adapter);
+
+/*
+ * Lookup an event timer adapter using its identifier.
+ *
+ * If an event timer adapter was created in another process with the same
+ * identifier, this function will locate its state and set up access to it
+ * so that it can be used in this process.
+ *
+ * @param adapter_id
+ * The event timer adapter identifier.
+ *
+ * @return
+ * A pointer to the event timer adapter matching the identifier on success.
+ * NULL on error with rte_errno set appropriately.
+ * Possible rte_errno values include:
+ * - ENOENT - required entry not available to return.
+ */
+struct rte_event_timer_adapter *rte_event_timer_adapter_lookup(
+ uint16_t adapter_id);
+
+/*
+ * Free an event timer adapter.
+ *
+ * Destroy an event timer adapter, freeing all resources.
+ *
+ * Before invoking this function, the application must wait for all the
+ * armed timers to expire or cancel the outstanding armed timers.
+ *
+ * @param adapter
+ * A pointer to an event timer adapter structure.
+ *
+ * @return
+ * - 0: Successfully freed the event timer adapter resources.
+ * - <0: Failed to free the event timer adapter resources.
+ * - -EAGAIN: adapter is busy; timers outstanding
+ * - -EBUSY: stop hasn't been called for this adapter yet
+ * - -EINVAL: adapter id invalid, or adapter invalid
+ */
+int rte_event_timer_adapter_free(struct rte_event_timer_adapter *adapter);
+
+/**
+ * Event timer state.
+ */
+enum rte_event_timer_state {
+ RTE_EVENT_TIMER_NOT_ARMED = 0,
+ /**< Event timer is in not armed state.*/
+ RTE_EVENT_TIMER_ARMED = 1,
+ /**< Event timer successfully armed.*/
+ RTE_EVENT_TIMER_ERROR = -1,
+ /**< Generic event timer error.*/
+ RTE_EVENT_TIMER_ERROR_TOOEARLY = -2,
+ /**< Event timer timeout tick is too little to add to the adapter. */
+ RTE_EVENT_TIMER_ERROR_TOOLATE = -3,
+ /**< Event timer timeout tick is greater than the maximum timeout.*/
+};
+
+/**
+ * The generic *rte_event_timer* structure to hold the event timer attributes
+ * for arm and cancel operations.
+ */
+RTE_STD_C11
+struct rte_event_timer {
+ struct rte_event ev;
+ /**<
+ * Expiry event attributes. On successful event timer timeout,
+ * the following attributes will be used to inject the expiry event to
+ * the eventdev:
+ * - event_queue_id: Targeted event queue id for expiry events.
+ * - event_priority: Event priority of the event expiry event in the
+ * event queue relative to other events.
+ * - sched_type: Scheduling type of the expiry event.
+ * - flow_id: Flow id of the expiry event.
+ * - op: RTE_EVENT_OP_NEW
+ * - event_type: RTE_EVENT_TYPE_TIMER
+ */
+ enum rte_event_timer_state state;
+ /**< State of the event timer. */
+ uint64_t timeout_ticks;
+ /**< Expiry timer ticks expressed in number of *timer_ticks_ns* from
+ * now.
+ * @see struct rte_event_timer_adapter_info::adapter_conf::timer_tick_ns
+ */
+ uint64_t impl_opaque[2];
+ /**< Implementation-specific opaque data.
+ * An event timer adapter implementation use this field to hold
+ * implementation specific values to share between the arm and cancel
+ * operations. The application should not modify this field.
+ */
+ uint8_t user_meta[];
+ /**< Memory to store user specific metadata.
+ * The event timer adapter implementation should not modify this area.
+ */
+} __rte_cache_aligned;
+
+/**
+ * Arm a burst of event timers with separate expiration timeout tick for each
+ * event timer.
+ *
+ * Before calling this function, the application allocates
+ * ``struct rte_event_timer`` objects from mempool or huge page backed
+ * application buffers of desired size. On successful allocation,
+ * application updates the `struct rte_event_timer`` attributes such as
+ * expiry event attributes, timeout ticks from now.
+ * This function submits the event timer arm requests to the event timer adapter
+ * and on expiry, the events will be injected to designated event queue.
+ *
+ * @param adapter
+ * A pointer to an event timer adapter structure.
+ * @param event_timers
+ * Pointer to an array of objects of type *rte_event_timer* structure.
+ * @param nb_event_timers
+ * Number of event timers in the supplied array.
+ *
+ * @return
+ * The number of successfully armed event timers. The return value can be less
+ * than the value of the *nb_timers* parameter. If the return value is less
+ * than *nb_events*, the remaining event timers at the end of *tim*
+ * are not consumed, and the caller has to take care of them, and rte_errno
+ * is set accordingly. Possible errno values include:
+ * - -EINVAL Invalid timer adapter identifier, expiry event queue ID is
+ * invalid, or an expiry event's sched type doesn't match the capabilities of
+ * the destination event queue.
+ * - -EAGAIN Specified timer adapter is not running
+ */
+int rte_event_timer_arm_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ uint16_t nb_event_timers);
+
+/**
+ * Arm a burst of event timers with same expiration timeout tick.
+ *
+ * Provides the same functionality as ``rte_event_timer_arm_burst()``, except
+ * that application can use this API when all the event timers have the
+ * same timeout expiration tick. This specialized function can provide the
+ * additional hint to the adapter implementation and optimize if possible.
+ *
+ * @param adapter
+ * A pointer to an event timer adapter structure.
+ * @param event_timers
+ * Points to an array of objects of type *rte_event_timer* structure.
+ * @param timeout_ticks
+ * The number of ticks in which the timers should expire.
+ * @param nb_event_timers
+ * Number of event timers in the supplied array.
+ *
+ * @return
+ * The number of successfully armed event timers. The return value can be less
+ * than the value of the *nb_timers* parameter. If the return value is less
+ * than *nb_events*, the remaining event timers at the end of *tim*
+ * are not consumed, and the caller has to take care of them, and rte_errno
+ * is set accordingly. Possible errno values include:
+ * - -EINVAL Invalid timer adapter identifier, expiry event queue ID is
+ * invalid, or an expiry event's sched type doesn't match the capabilities of
+ * the destination event queue.
+ * - -EAGAIN Specified event timer adapter is not running
+ */
+int rte_event_timer_arm_tmo_tick_burst(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ const uint64_t timeout_ticks,
+ const uint16_t nb_event_timers);
+
+/**
+ * Cancel a burst of event timer from being scheduled to the event device.
+ *
+ * @param adapter
+ * A pointer to an event timer adapter structure.
+ * @param event_timers
+ * Points to an array of objects of type *rte_event_timer* structure
+ * @param nb_event_timers
+ * Number of event timer instances in the supplied array.
+ *
+ * @return
+ * The number of successfully canceled event timers. The return value can be
+ * less than the value of the *nb_timers* parameter. If the return value is
+ * less than *nb_events*, the remaining event timers at the end of *tim*
+ * are not consumed, and the caller has to take care of them, and rte_errno
+ * is set accordingly. Possible errno values include:
+ * - -EINVAL Invalid timer adapter identifier
+ * - -EAGAIN Specified timer adapter is not running
+ */
+int rte_event_timer_cancel_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ uint16_t nb_event_timers);
+
+#endif /* __RTE_EVENT_TIMER_ADAPTER_H__ */
diff --git a/lib/librte_eventdev/rte_event_timer_adapter_driver.h b/lib/librte_eventdev/rte_event_timer_adapter_driver.h
new file mode 100644
index 0000000..485fad1
--- /dev/null
+++ b/lib/librte_eventdev/rte_event_timer_adapter_driver.h
@@ -0,0 +1,159 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __RTE_EVENT_TIMER_ADAPTER_DRIVER_H__
+#define __RTE_EVENT_TIMER_ADAPTER_DRIVER_H__
+
+/**
+ * @file
+ *
+ * Description
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "rte_event_timer_adapter.h"
+
+/*
+ * Definitions of functions exported by an event timer adapter implementation
+ * through *rte_event_timer_adapter_ops* structure supplied in the
+ * *rte_event_timer_adapter* structure associated with an event timer adapter.
+ */
+
+typedef int (*rte_event_timer_adapter_init_t)(
+ struct rte_event_timer_adapter *adapter);
+/**< @internal Event timer adapter implementation setup */
+typedef int (*rte_event_timer_adapter_uninit_t)(
+ struct rte_event_timer_adapter *adapter);
+/**< @internal Event timer adapter implementation teardown */
+typedef int (*rte_event_timer_adapter_start_t)(
+ const struct rte_event_timer_adapter *adapter);
+/**< @internal Start running event timer adapter */
+typedef int (*rte_event_timer_adapter_stop_t)(
+ const struct rte_event_timer_adapter *adapter);
+/**< @internal Stop running event timer adapter */
+typedef void (*rte_event_timer_adapter_get_info_t)(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer_adapter_info *adapter_info);
+/**< @internal Get contextual information for event timer adapter */
+typedef int (*rte_event_timer_arm_burst_t)(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ uint16_t nb_tims);
+/**< @internal Enable event timers to enqueue timer events upon expiry */
+typedef int (*rte_event_timer_arm_tmo_tick_burst_t)(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ uint64_t timeout_tick,
+ uint16_t nb_tims);
+/**< @internal Enable event timers with common expiration time */
+typedef int (*rte_event_timer_cancel_burst_t)(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ uint16_t nb_tims);
+/**< @internal Prevent event timers from enqueuing timer events */
+
+/**
+ * @internal Structure containing the functions exported by an event timer
+ * adapter implementation.
+ */
+struct rte_event_timer_adapter_ops {
+ rte_event_timer_adapter_init_t init; /**< Set up adapter */
+ rte_event_timer_adapter_uninit_t uninit;/**< Tear down adapter */
+ rte_event_timer_adapter_start_t start; /**< Start adapter */
+ rte_event_timer_adapter_stop_t stop; /**< Stop adapter */
+ rte_event_timer_adapter_get_info_t get_info;
+ /**< Get info from driver */
+ rte_event_timer_arm_burst_t arm_burst;
+ /**< Arm one or more event timers */
+ rte_event_timer_arm_tmo_tick_burst_t arm_tmo_tick_burst;
+ /**< Arm event timers with same expiration time */
+ rte_event_timer_cancel_burst_t cancel_burst;
+ /**< Cancel one or more event timers */
+};
+
+/**
+ * @internal Adapter data; structure to be placed in shared memory to be
+ * accessible by various processes in a multi-process configuration.
+ */
+struct rte_event_timer_adapter_data {
+ uint8_t id;
+ /**< Event timer adapter ID */
+ uint8_t event_dev_id;
+ /**< Event device ID */
+ uint32_t socket_id;
+ /**< Socket ID where memory is allocated */
+ uint8_t event_port_id;
+ /**< Optional: event port ID used when the inbuilt port is absent */
+ const struct rte_memzone *mz;
+ /**< Event timer adapter memzone pointer */
+ struct rte_event_timer_adapter_conf conf;
+ /**< Configuration used to configure the adapter. */
+ uint32_t caps;
+ /**< Adapter capabilities */
+ void *adapter_priv;
+ /**< Timer adapter private data*/
+
+ RTE_STD_C11
+ uint8_t started : 1;
+ /**< Flag to indicate adapter started. */
+} __rte_cache_aligned;
+
+/**
+ * @internal Data structure associated with each event timer adapter.
+ */
+struct rte_event_timer_adapter {
+ rte_event_timer_arm_burst_t arm_burst;
+ /**< Pointer to driver arm_burst function. */
+ rte_event_timer_arm_tmo_tick_burst_t arm_tmo_tick_burst;
+ /**< Pointer to driver arm_tmo_tick_burst function. */
+ rte_event_timer_cancel_burst_t cancel_burst;
+ /**< Pointer to driver cancel function. */
+
+ struct rte_event_timer_adapter_data *data;
+ /**< Pointer to shared adapter data */
+ const struct rte_event_timer_adapter_ops *ops;
+ /**< Functions exported by adapter driver */
+
+ RTE_STD_C11
+ uint8_t allocated : 1;
+ /**< Flag to indicate that this adapter has been allocated */
+} __rte_cache_aligned;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RTE_EVENT_TIMER_ADAPTER_DRIVER_H__ */
diff --git a/lib/librte_eventdev/rte_eventdev.h b/lib/librte_eventdev/rte_eventdev.h
index f1949ff..67360c1 100644
--- a/lib/librte_eventdev/rte_eventdev.h
+++ b/lib/librte_eventdev/rte_eventdev.h
@@ -1025,6 +1025,9 @@ struct rte_event {
* @see struct rte_event_eth_rx_adapter_queue_conf::rx_queue_flags
*/

+#define RTE_EVENT_TIMER_ADAPTER_CAP_INTERNAL_PORT (1ULL << 1)
+/**< This flag is set when the timer mechanism is in HW. */
+
/**
* Retrieve the event device's ethdev Rx adapter capabilities for the
* specified ethernet port
diff --git a/lib/librte_eventdev/rte_eventdev_pmd.h b/lib/librte_eventdev/rte_eventdev_pmd.h
index 7a206c5..719fde3 100644
--- a/lib/librte_eventdev/rte_eventdev_pmd.h
+++ b/lib/librte_eventdev/rte_eventdev_pmd.h
@@ -52,6 +52,7 @@ extern "C" {
#include <rte_malloc.h>

#include "rte_eventdev.h"
+#include "rte_event_timer_adapter_driver.h"

/* Logging Macros */
#define RTE_EDEV_LOG_ERR(...) \
@@ -466,6 +467,9 @@ typedef int (*eventdev_eth_rx_adapter_caps_get_t)

struct rte_event_eth_rx_adapter_queue_conf *queue_conf;

+typedef int (*eventdev_timer_adapter_caps_get_t)(const struct rte_eventdev *dev,
+ uint32_t *caps, const struct rte_event_timer_adapter_ops **ops);
+
/**
* Add ethernet Rx queues to event device. This callback is invoked if
* the caps returned from rte_eventdev_eth_rx_adapter_caps_get(, eth_port_id)
@@ -650,6 +654,9 @@ struct rte_eventdev_ops {
/**< Get ethernet Rx stats */
eventdev_eth_rx_adapter_stats_reset eth_rx_adapter_stats_reset;
/**< Reset ethernet Rx stats */
+
+ eventdev_timer_adapter_caps_get_t timer_adapter_caps_get;
+ /**< Get timer adapter capabilities */
};

/**
diff --git a/lib/librte_eventdev/rte_eventdev_version.map b/lib/librte_eventdev/rte_eventdev_version.map
index 108ae61..f56ca0f 100644
--- a/lib/librte_eventdev/rte_eventdev_version.map
+++ b/lib/librte_eventdev/rte_eventdev_version.map
@@ -66,5 +66,18 @@ DPDK_17.11 {
rte_event_eth_rx_adapter_stats_get;
rte_event_eth_rx_adapter_stats_reset;
rte_event_eth_rx_adapter_stop;
-
} DPDK_17.08;
+
+DPDK_18.02 {
+ global:
+
+ rte_event_timer_adapter_create;
+ rte_event_timer_adapter_create_ext;
+ rte_event_timer_adapter_free;
+ rte_event_timer_adapter_get_info;
+ rte_event_timer_adapter_start;
+ rte_event_timer_adapter_stop;
+ rte_event_timer_arm_burst;
+ rte_event_timer_arm_tmo_tick_burst;
+ rte_event_timer_cancel_burst;
+} DPDK_17.11;
diff --git a/lib/librte_eventdev/sw_event_timer_adapter.c b/lib/librte_eventdev/sw_event_timer_adapter.c
new file mode 100644
index 0000000..43fdad2
--- /dev/null
+++ b/lib/librte_eventdev/sw_event_timer_adapter.c
@@ -0,0 +1,123 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "rte_event_timer_adapter.h"
+#include "rte_event_timer_adapter_driver.h"
+
+static int
+sw_event_timer_adapter_init(struct rte_event_timer_adapter *adapter)
+{
+ RTE_SET_USED(adapter);
+
+ return 0;
+}
+
+static int
+sw_event_timer_adapter_uninit(struct rte_event_timer_adapter *adapter)
+{
+ RTE_SET_USED(adapter);
+
+ return 0;
+}
+
+static int
+sw_event_timer_adapter_start(const struct rte_event_timer_adapter *adapter)
+{
+ RTE_SET_USED(adapter);
+
+ return 0;
+}
+
+static int
+sw_event_timer_adapter_stop(const struct rte_event_timer_adapter *adapter)
+{
+ RTE_SET_USED(adapter);
+
+ return 0;
+}
+
+static void
+sw_event_timer_adapter_get_info(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer_adapter_info *adapter_info)
+{
+ RTE_SET_USED(adapter);
+ RTE_SET_USED(adapter_info);
+}
+
+static int
+sw_event_timer_arm_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **evtims,
+ uint16_t nb_evtims)
+{
+ RTE_SET_USED(adapter);
+ RTE_SET_USED(evtims);
+ RTE_SET_USED(nb_evtims);
+
+ return 0;
+}
+
+static int
+sw_event_timer_cancel_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **evtims,
+ uint16_t nb_evtims)
+{
+ RTE_SET_USED(adapter);
+ RTE_SET_USED(evtims);
+ RTE_SET_USED(nb_evtims);
+
+ return 0;
+}
+
+static int
+sw_event_timer_arm_tmo_tick_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ uint64_t timeout_tick,
+ uint16_t nb_tims)
+{
+ RTE_SET_USED(adapter);
+ RTE_SET_USED(tims);
+ RTE_SET_USED(timeout_tick);
+ RTE_SET_USED(nb_tims);
+
+ return 0;
+}
+
+const struct rte_event_timer_adapter_ops sw_event_adapter_timer_ops = {
+ .init = sw_event_timer_adapter_init,
+ .uninit = sw_event_timer_adapter_uninit,
+ .start = sw_event_timer_adapter_start,
+ .stop = sw_event_timer_adapter_stop,
+ .get_info = sw_event_timer_adapter_get_info,
+ .arm_burst = sw_event_timer_arm_burst,
+ .arm_tmo_tick_burst = sw_event_timer_arm_tmo_tick_burst,
+ .cancel_burst = sw_event_timer_cancel_burst,
+};
diff --git a/test/test/Makefile b/test/test/Makefile
index bb54c98..9448aef 100644
--- a/test/test/Makefile
+++ b/test/test/Makefile
@@ -210,6 +210,7 @@ ifeq ($(CONFIG_RTE_LIBRTE_EVENTDEV),y)
SRCS-y += test_eventdev.c
SRCS-y += test_event_ring.c
SRCS-y += test_event_eth_rx_adapter.c
+SRCS-y += test_event_timer_adapter.c
SRCS-$(CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV) += test_eventdev_sw.c
SRCS-$(CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF) += test_eventdev_octeontx.c
endif
diff --git a/test/test/test_event_timer_adapter.c b/test/test/test_event_timer_adapter.c
new file mode 100644
index 0000000..d0ea066
--- /dev/null
+++ b/test/test/test_event_timer_adapter.c
@@ -0,0 +1,249 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include <rte_eventdev.h>
+#include <rte_dev.h>
+#include <rte_bus_vdev.h>
+#include <rte_event_timer_adapter.h>
+#include <rte_mempool.h>
+#include <rte_errno.h>
+#include <rte_service_component.h>
+
+#include "test.h"
+
+/* Example from RFC */
+#define NB_TEST_EVENT_TIMERS 40000
+
+static int evdev;
+//struct rte_event_timer_adapter *g_adapter;
+struct rte_event_timer *g_evtimer;
+struct rte_mempool *g_event_timer_pool;
+
+static inline void
+devconf_set_default_sane_values(struct rte_event_dev_config *dev_conf,
+ struct rte_event_dev_info *info)
+{
+ memset(dev_conf, 0, sizeof(struct rte_event_dev_config));
+ dev_conf->dequeue_timeout_ns = info->min_dequeue_timeout_ns;
+ /* Leave a port for the adapter to allocate */
+ dev_conf->nb_event_ports = info->max_event_ports - 1;
+ dev_conf->nb_event_queues = info->max_event_queues;
+ dev_conf->nb_event_queue_flows = info->max_event_queue_flows;
+ dev_conf->nb_event_port_dequeue_depth =
+ info->max_event_port_dequeue_depth;
+ dev_conf->nb_event_port_enqueue_depth =
+ info->max_event_port_enqueue_depth;
+ dev_conf->nb_event_port_enqueue_depth =
+ info->max_event_port_enqueue_depth;
+ dev_conf->nb_events_limit =
+ info->max_num_events;
+}
+
+static int
+configure_event_dev(void)
+{
+ struct rte_event_dev_config devconf;
+ int ret;
+ const char *eventdev_name = "event_sw0";
+ struct rte_event_dev_info info;
+ int i;
+
+ evdev = rte_event_dev_get_dev_id(eventdev_name);
+ if (evdev < 0) {
+ if (rte_vdev_init(eventdev_name, NULL) < 0) {
+ printf("Error creating eventdev\n");
+ return TEST_FAILED;
+ }
+ evdev = rte_event_dev_get_dev_id(eventdev_name);
+ if (evdev < 0) {
+ printf("Error finding newly created eventdev\n");
+ return TEST_FAILED;
+ }
+ }
+
+ ret = rte_event_dev_info_get(evdev, &info);
+ TEST_ASSERT_SUCCESS(ret, "Failed to get event dev info");
+
+ devconf_set_default_sane_values(&devconf, &info);
+
+ ret = rte_event_dev_configure(evdev, &devconf);
+ TEST_ASSERT_SUCCESS(ret, "Failed to configure eventdev");
+
+ /* Map the event_sw0 service to a service core */
+ ret = rte_service_start_with_defaults();
+ TEST_ASSERT_SUCCESS(ret, "Failed to start sw_evdev service");
+
+ /* Set up event queues */
+ uint32_t queue_count;
+ TEST_ASSERT_SUCCESS(rte_event_dev_attr_get(evdev,
+ RTE_EVENT_DEV_ATTR_QUEUE_COUNT, &queue_count),
+ "Queue count get failed");
+
+ for (i = 0; i < (int)queue_count; i++) {
+ ret = rte_event_queue_setup(evdev, i, NULL);
+ TEST_ASSERT_SUCCESS(ret, "Failed to setup queue=%d", i);
+ }
+
+ /* Set up event ports */
+ uint32_t port_count;
+ TEST_ASSERT_SUCCESS(rte_event_dev_attr_get(evdev,
+ RTE_EVENT_DEV_ATTR_PORT_COUNT,
+ &port_count), "Port count get failed");
+
+ for (i = 0; i < (int)port_count; i++) {
+ ret = rte_event_port_setup(evdev, i, NULL);
+ TEST_ASSERT_SUCCESS(ret, "Failed to setup port=%d", i);
+ /* Link each queues to all ports */
+ ret = rte_event_port_link(evdev, i, NULL, NULL, 0);
+ TEST_ASSERT(ret >= 0, "Failed to link all queues port=%d", i);
+ }
+
+ /* Start the event device */
+ ret = rte_event_dev_start(evdev);
+ TEST_ASSERT_SUCCESS(ret, "Failed to start device");
+
+ return TEST_SUCCESS;
+}
+
+static int
+testsuite_setup(void)
+{
+ int ret;
+
+ /* Setup and start event device. */
+ ret = configure_event_dev();
+ if (ret) {
+ printf("Failed to configure event dev\n");
+ return TEST_FAILED;
+ }
+
+ /* Create a mempool of event timers. */
+ g_event_timer_pool = rte_mempool_create("event_timer_mempool",
+ NB_TEST_EVENT_TIMERS,
+ sizeof(struct rte_event_timer),
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ rte_socket_id(),
+ 0);
+ if (g_event_timer_pool == NULL) {
+ /* Failed to create event timer mempool. */
+ printf("Failed to configure event timer mempool: %s\n",
+ rte_strerror(rte_errno));
+ return TEST_FAILED;
+ }
+
+ return TEST_SUCCESS;
+}
+
+static void
+testsuite_teardown(void)
+{
+ /* TODO: tear down adapter and evdev */
+
+ rte_mempool_free(g_event_timer_pool);
+}
+
+#define NSECPERSEC 1E9 // No of ns for 1 sec
+
+static int
+adapter_create_free(void)
+{
+ int ret;
+ int adapter_id = 0;
+ struct rte_event_timer_adapter *adapter;
+
+ struct rte_event_timer_adapter_conf conf = {
+ .event_dev_id = evdev,
+ .timer_adapter_id = adapter_id,
+ .clk_src = RTE_EVENT_TIMER_ADAPTER_CPU_CLK,
+ .timer_tick_ns = NSECPERSEC / 10, // 100 milliseconds
+ .max_tmo_ns = 180 * NSECPERSEC, // 2 minutes
+ .nb_timers = NB_TEST_EVENT_TIMERS,
+ .flags = 0,
+ };
+
+ adapter = rte_event_timer_adapter_create(&conf);
+ if (adapter == NULL) {
+ printf("Failed to create adapter\n");
+ return TEST_FAILED;
+ }
+
+ /* Move to separate tests later; just verify plugin connections for
+ * now
+ */
+
+ struct rte_event_timer_adapter_info adapter_info;
+ ret = rte_event_timer_adapter_get_info(adapter, &adapter_info);
+ if (ret < 0)
+ return TEST_FAILED;
+
+ ret = rte_event_timer_adapter_start(adapter);
+ if (ret < 0)
+ return TEST_FAILED;
+
+ ret = rte_event_timer_adapter_stop(adapter);
+ if (ret < 0)
+ return TEST_FAILED;
+
+ ret = rte_event_timer_adapter_free(adapter);
+ if (ret) {
+ printf("Failed to free adapter\n");
+ return TEST_FAILED;
+ }
+
+ return TEST_SUCCESS;
+}
+
+static struct unit_test_suite adapter_tests = {
+ .suite_name = "event timer adapter test suite",
+ .setup = testsuite_setup,
+ .teardown = testsuite_teardown,
+ .unit_test_cases = {
+ TEST_CASE(adapter_create_free),
+ TEST_CASES_END() /**< NULL terminate unit test array */
+ }
+};
+
+static int
+test_event_timer_adapter_common(void)
+{
+ return unit_test_suite_runner(&adapter_tests);
+}
+
+REGISTER_TEST_COMMAND(event_timer_adapter_autotest,
+ test_event_timer_adapter_common);
--
2.6.4
Pavan Nikhilesh Bhagavatula
2017-11-23 04:37:08 UTC
Permalink
Hi Gabriel,

Can you please split the patch into smaller chunks so that it will be easy to
review and provide comments.

Regards,
Pavan.
Post by Erik Gabriel Carrillo
---
v3
- Reworked allocation and ops organization in common code based on feedback
received from Jerin and Pavan. This will allow fast-path function pointers to
be dereferenced with one level of indirection with pointers valid in primary
and secondary processes.
- Moved default software implementation from sw_evdev directory to eventdev
library directory, which will allow it to be used by any eventdev PMD as an
alternative to providing its own definitions.
- Reverted occurrences of id back to pointer to adapter struct in library API
- Added rte_event_timer_adapter_lookup() function back in
Carrillo, Erik G
2017-11-27 14:47:30 UTC
Permalink
Hi Pavan,

Sure, will do.

Thanks,
Gabriel
-----Original Message-----
From: Pavan Nikhilesh Bhagavatula
Sent: Wednesday, November 22, 2017 10:37 PM
Subject: Re: [RFC PATCH v3 1/1] eventtimer: introduce event timer adapter
Hi Gabriel,
Can you please split the patch into smaller chunks so that it will be easy to
review and provide comments.
Regards,
Pavan.
Post by Erik Gabriel Carrillo
---
v3
- Reworked allocation and ops organization in common code based on
feedback
Post by Erik Gabriel Carrillo
received from Jerin and Pavan. This will allow fast-path function pointers
to
Post by Erik Gabriel Carrillo
be dereferenced with one level of indirection with pointers valid in
primary
Post by Erik Gabriel Carrillo
and secondary processes.
- Moved default software implementation from sw_evdev directory to
eventdev
Post by Erik Gabriel Carrillo
library directory, which will allow it to be used by any eventdev PMD as an
alternative to providing its own definitions.
- Reverted occurrences of id back to pointer to adapter struct in library API
- Added rte_event_timer_adapter_lookup() function back in
Erik Gabriel Carrillo
2017-11-28 17:40:04 UTC
Permalink
This set of RFC patches contains the same changes as the previous version,
but they are now broken up into multiple patches for easier review.

This patch set produces the same checkpatch warnings as the previous version:
"macro with flow control" and "avoid externs". I have left the macros in
since such usage seems common in DPDK, and left the extern in the .c because
it is the only file that uses that symbol, and it seems better than creating
a new header file that only contains the extern var.

v4
- Split changes into multiple patches for easier review

v3
- Reworked allocation and ops organization in common code based on feedback
received from Jerin and Pavan. This will allow fast-path function pointers to
be dereferenced with one level of indirection with pointers valid in primary
and secondary processes.
- Moved default software implementation from sw_evdev directory to eventdev
library directory, which will allow it to be used by any eventdev PMD as an
alternative to providing its own definitions.
- Reverted occurrences of id back to pointer to adapter struct in library API
- Added rte_event_timer_adapter_lookup() function back in

v2
- Added ops structure and stubbed out plugin for SW impl
- Added unit test stubs
- Replaced occurrences of "wheel" in API with "adapter"
- Replaced occurrences of pointer to struct rte_event_timer_adapter with ids
- Removed rte_event_timer_adapter_lookup() function
- Replaced RTE_EVENT_TIMER_SUCCESS_{ARM,CANCEL} states with
RTE_EVENT_TIMER_ARMED

Erik Gabriel Carrillo (4):
eventtimer: introduce event timer adapter
eventtimer: add common code
eventtimer: add default software implementation stub
test: add event timer adapter auto-test

doc/api/doxy-api-index.md | 1 +
drivers/event/sw/sw_evdev.c | 16 +
lib/librte_eventdev/Makefile | 4 +
lib/librte_eventdev/rte_event_timer_adapter.c | 428 +++++++++++++++++++++
lib/librte_eventdev/rte_event_timer_adapter.h | 427 ++++++++++++++++++++
.../rte_event_timer_adapter_driver.h | 159 ++++++++
lib/librte_eventdev/rte_eventdev.h | 7 +-
lib/librte_eventdev/rte_eventdev_pmd.h | 7 +
lib/librte_eventdev/rte_eventdev_version.map | 15 +-
lib/librte_eventdev/sw_event_timer_adapter.c | 123 ++++++
test/test/Makefile | 1 +
test/test/test_event_timer_adapter.c | 249 ++++++++++++
12 files changed, 1434 insertions(+), 3 deletions(-)
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter.c
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter.h
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter_driver.h
create mode 100644 lib/librte_eventdev/sw_event_timer_adapter.c
create mode 100644 test/test/test_event_timer_adapter.c
--
2.6.4
Erik Gabriel Carrillo
2017-11-28 17:40:05 UTC
Permalink
Signed-off-by: Erik Gabriel Carrillo <***@intel.com>
---
doc/api/doxy-api-index.md | 1 +
lib/librte_eventdev/Makefile | 1 +
lib/librte_eventdev/rte_event_timer_adapter.h | 427 ++++++++++++++++++++++++++
lib/librte_eventdev/rte_eventdev.h | 4 +-
4 files changed, 431 insertions(+), 2 deletions(-)
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 3492702..3110658 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -46,6 +46,7 @@ The public API headers are grouped by topics:
[security] (@ref rte_security.h),
[eventdev] (@ref rte_eventdev.h),
[event_eth_rx_adapter] (@ref rte_event_eth_rx_adapter.h),
+ [event_timer_adapter] (@ref rte_event_timer_adapter.h),
[metrics] (@ref rte_metrics.h),
[bitrate] (@ref rte_bitrate.h),
[latency] (@ref rte_latencystats.h),
diff --git a/lib/librte_eventdev/Makefile b/lib/librte_eventdev/Makefile
index 5ac22cd..6ef7c1c 100644
--- a/lib/librte_eventdev/Makefile
+++ b/lib/librte_eventdev/Makefile
@@ -53,6 +53,7 @@ SYMLINK-y-include += rte_eventdev_pmd_pci.h
SYMLINK-y-include += rte_eventdev_pmd_vdev.h
SYMLINK-y-include += rte_event_ring.h
SYMLINK-y-include += rte_event_eth_rx_adapter.h
+SYMLINK-y-include += rte_event_timer_adapter.h

# versioning export map
EXPORT_MAP := rte_eventdev_version.map
diff --git a/lib/librte_eventdev/rte_event_timer_adapter.h b/lib/librte_eventdev/rte_event_timer_adapter.h
new file mode 100644
index 0000000..14661b5
--- /dev/null
+++ b/lib/librte_eventdev/rte_event_timer_adapter.h
@@ -0,0 +1,427 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __RTE_EVENT_TIMER_ADAPTER_H__
+#define __RTE_EVENT_TIMER_ADAPTER_H__
+
+/**
+ * @file
+ *
+ * RTE Event Timer Adapter
+ *
+ * TODO: description
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <rte_spinlock.h>
+#include <rte_memory.h>
+
+#include "rte_eventdev.h"
+
+#define RTE_EVENT_TIMER_ADAPTER_NUM_MAX 64
+
+/**
+ * Timer adapter clock source
+ */
+enum rte_event_timer_adapter_clk_src {
+ RTE_EVENT_TIMER_ADAPTER_CPU_CLK,
+ /**< Use CPU clock as the clock source. */
+ RTE_EVENT_TIMER_ADAPTER_EXT_CLK0,
+ /**< Platform dependent external clock source 0. */
+ RTE_EVENT_TIMER_ADAPTER_EXT_CLK1,
+ /**< Platform dependent external clock source 1. */
+ RTE_EVENT_TIMER_ADAPTER_EXT_CLK2,
+ /**< Platform dependent external clock source 2. */
+ RTE_EVENT_TIMER_ADAPTER_EXT_CLK3,
+ /**< Platform dependent external clock source 3. */
+};
+
+#define RTE_EVENT_TIMER_ADAPTER_F_ADJUST_RES (1ULL << 0)
+/**< The event timer adapter implementation may have constraints on the
+ * resolution (timer_tick_ns) and maximum timer expiry timeout(max_tmo_ns)
+ * based on the given timer adapter or system. If this flag is set, the
+ * implementation adjusts the resolution and maximum timeout to the best
+ * possible configuration. On successful timer adapter creation, the
+ * application can get the configured resolution and max timeout with
+ * ``rte_event_timer_adapter_get_info()``.
+ */
+#define RTE_EVENT_TIMER_ADAPTER_F_SP_PUT (1ULL << 1)
+/**< ``rte_event_timer_arm_burst()`` API to be used in single producer mode.
+ *
+ * @see struct rte_event_timer_adapter_conf::flags
+ */
+
+/*
+ * Timer adapter configuration structure
+ */
+struct rte_event_timer_adapter_conf {
+ uint8_t event_dev_id;
+ /**< Event device identifier */
+ uint16_t timer_adapter_id;
+ /**< Event timer adapter identifier */
+ uint32_t socket_id;
+ /**< Identifer of socket from which to allocate memory for adapter */
+ enum rte_event_timer_adapter_clk_src clk_src;
+ /**< Clock source for timer adapter */
+ uint64_t timer_tick_ns;
+ /**< Timer adapter resolution in ns */
+ uint64_t max_tmo_ns;
+ /**< Maximum timer timeout(expiry) in ns */
+ uint64_t nb_timers;
+ /**< Total number of timers per adapter */
+ uint64_t flags;
+ /**< Timer adapter config flags (RTE_EVENT_TIMER_ADAPTER_F_*) */
+};
+
+struct rte_event_timer_adapter;
+
+/*
+ * Callback function type for producer port creation.
+ */
+typedef int (*rte_event_timer_adapter_port_conf_cb_t)(uint16_t id,
+ uint8_t event_dev_id,
+ uint8_t *event_port_id,
+ void *conf_arg);
+
+/*
+ * Create an event timer adapter.
+ *
+ * This function must be invoked first before any other function in the API.
+ *
+ * @param conf
+ * The event timer adapter configuration structure.
+ *
+ * @return
+ * A pointer to the new allocated event timer adapter on success.
+ * NULL on error with rte_errno set appropriately.
+ * Possible rte_errno values include:
+ * - ERANGE: timer_tick_ns is not in supported range.
+ */
+struct rte_event_timer_adapter *rte_event_timer_adapter_create(
+ const struct rte_event_timer_adapter_conf *conf);
+
+/*
+ * Create a timer adapter with the supplied callback.
+ *
+ * This function can be used to have a more granular control over the timer
+ * adapter creation. If a built-in port is absent, then the function uses the
+ * callback provided to create and get the port id to be used as a producer
+ * port.
+ *
+ * @param conf
+ * The timer adapter configuration structure
+ * @param conf_cb
+ * The port config callback function.
+ * @param conf_arg
+ * Opaque pointer to the argument for the callback function
+ * @param id_ptr[out]
+ * Address of variable to store adapter identifier in
+ *
+ * @return
+ * A pointer to the new allocated event timer adapter on success.
+ * NULL on error with rte_errno set appropriately.
+ * Possible rte_errno values include:
+ * - ERANGE: timer_tick_ns is not in supported range.
+ * - ENOMEM: unable to allocate sufficient memory for adapter instances
+ * - EINVAL: invalid event device identifier specified in config
+ * - ENOSPC: maximum number of adapters already created
+ */
+struct rte_event_timer_adapter *rte_event_timer_adapter_create_ext(
+ const struct rte_event_timer_adapter_conf *conf,
+ rte_event_timer_adapter_port_conf_cb_t conf_cb,
+ void *conf_arg);
+
+/*
+ * Timer adapter info structure.
+ */
+struct rte_event_timer_adapter_info {
+ uint64_t min_resolution_ns;
+ /**< Minimum timer adapter resolution in ns */
+ uint64_t max_tmo_ns;
+ /**< Maximum timer timeout(expire) in ns */
+ struct rte_event_timer_adapter_conf conf;
+ /**< Configured timer adapter attributes */
+ uint32_t caps;
+ /**< Event timer adapter capabilities */
+ int16_t event_dev_port_id;
+ /**< Event device port ID, if applicable */
+ int32_t service_id;
+ /**< Service ID, if applicable */
+};
+
+/**
+ * Retrieve the contextual information of an event timer adapter.
+ *
+ * @param adapter
+ * A pointer to the event timer adapter structure.
+ *
+ * @param[out] adapter_info
+ * A pointer to a structure of type *rte_event_timer_adapter_info* to be
+ * filled with the contextual information of the adapter.
+ *
+ * @return
+ * - 0: Success, driver updates the contextual information of the
+ * timer adapter
+ * - <0: Error code returned by the driver info get function.
+ * - -EINVAL if adapter identifier invalid
+ *
+ * @see RTE_EVENT_TIMER_ADAPTER_F_ADJUST_RES,
+ * struct rte_event_timer_adapter_info
+ *
+ */
+int rte_event_timer_adapter_get_info(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer_adapter_info *adapter_info);
+
+/**
+ * Start a timer adapter.
+ *
+ * The adapter start step is the last one and consists of setting the timer
+ * adapter to start accepting the timers and schedules to event queues.
+ *
+ * On success, all basic functions exported by the API (timer arm,
+ * timer cancel and so on) can be invoked.
+ *
+ * @param adapter
+ * A pointer to the event timer adapter structure.
+ *
+ * @return
+ * - 0: Success, adapter started.
+ * - <0: Error code returned by the driver start function.
+ * - -EINVAL if adapter identifier invalid
+ */
+int rte_event_timer_adapter_start(
+ const struct rte_event_timer_adapter *adapter);
+
+/**
+ * Stop an event timer adapter.
+ *
+ * The adapter can be restarted with a call to
+ * ``rte_event_timer_adapter_start()``.
+ *
+ * @param adapter
+ * A pointer to the event timer adapter structure.
+ *
+ * @return
+ * - 0: Success, adapter stopped.
+ * - <0: Error code returned by the driver stop function.
+ * - -EINVAL if adapter identifier invalid
+ */
+int rte_event_timer_adapter_stop(const struct rte_event_timer_adapter *adapter);
+
+/*
+ * Lookup an event timer adapter using its identifier.
+ *
+ * If an event timer adapter was created in another process with the same
+ * identifier, this function will locate its state and set up access to it
+ * so that it can be used in this process.
+ *
+ * @param adapter_id
+ * The event timer adapter identifier.
+ *
+ * @return
+ * A pointer to the event timer adapter matching the identifier on success.
+ * NULL on error with rte_errno set appropriately.
+ * Possible rte_errno values include:
+ * - ENOENT - required entry not available to return.
+ */
+struct rte_event_timer_adapter *rte_event_timer_adapter_lookup(
+ uint16_t adapter_id);
+
+/*
+ * Free an event timer adapter.
+ *
+ * Destroy an event timer adapter, freeing all resources.
+ *
+ * Before invoking this function, the application must wait for all the
+ * armed timers to expire or cancel the outstanding armed timers.
+ *
+ * @param adapter
+ * A pointer to an event timer adapter structure.
+ *
+ * @return
+ * - 0: Successfully freed the event timer adapter resources.
+ * - <0: Failed to free the event timer adapter resources.
+ * - -EAGAIN: adapter is busy; timers outstanding
+ * - -EBUSY: stop hasn't been called for this adapter yet
+ * - -EINVAL: adapter id invalid, or adapter invalid
+ */
+int rte_event_timer_adapter_free(struct rte_event_timer_adapter *adapter);
+
+/**
+ * Event timer state.
+ */
+enum rte_event_timer_state {
+ RTE_EVENT_TIMER_NOT_ARMED = 0,
+ /**< Event timer is in not armed state.*/
+ RTE_EVENT_TIMER_ARMED = 1,
+ /**< Event timer successfully armed.*/
+ RTE_EVENT_TIMER_ERROR = -1,
+ /**< Generic event timer error.*/
+ RTE_EVENT_TIMER_ERROR_TOOEARLY = -2,
+ /**< Event timer timeout tick is too little to add to the adapter. */
+ RTE_EVENT_TIMER_ERROR_TOOLATE = -3,
+ /**< Event timer timeout tick is greater than the maximum timeout.*/
+};
+
+/**
+ * The generic *rte_event_timer* structure to hold the event timer attributes
+ * for arm and cancel operations.
+ */
+RTE_STD_C11
+struct rte_event_timer {
+ struct rte_event ev;
+ /**<
+ * Expiry event attributes. On successful event timer timeout,
+ * the following attributes will be used to inject the expiry event to
+ * the eventdev:
+ * - event_queue_id: Targeted event queue id for expiry events.
+ * - event_priority: Event priority of the event expiry event in the
+ * event queue relative to other events.
+ * - sched_type: Scheduling type of the expiry event.
+ * - flow_id: Flow id of the expiry event.
+ * - op: RTE_EVENT_OP_NEW
+ * - event_type: RTE_EVENT_TYPE_TIMER
+ */
+ enum rte_event_timer_state state;
+ /**< State of the event timer. */
+ uint64_t timeout_ticks;
+ /**< Expiry timer ticks expressed in number of *timer_ticks_ns* from
+ * now.
+ * @see struct rte_event_timer_adapter_info::adapter_conf::timer_tick_ns
+ */
+ uint64_t impl_opaque[2];
+ /**< Implementation-specific opaque data.
+ * An event timer adapter implementation use this field to hold
+ * implementation specific values to share between the arm and cancel
+ * operations. The application should not modify this field.
+ */
+ uint8_t user_meta[];
+ /**< Memory to store user specific metadata.
+ * The event timer adapter implementation should not modify this area.
+ */
+} __rte_cache_aligned;
+
+/**
+ * Arm a burst of event timers with separate expiration timeout tick for each
+ * event timer.
+ *
+ * Before calling this function, the application allocates
+ * ``struct rte_event_timer`` objects from mempool or huge page backed
+ * application buffers of desired size. On successful allocation,
+ * application updates the `struct rte_event_timer`` attributes such as
+ * expiry event attributes, timeout ticks from now.
+ * This function submits the event timer arm requests to the event timer adapter
+ * and on expiry, the events will be injected to designated event queue.
+ *
+ * @param adapter
+ * A pointer to an event timer adapter structure.
+ * @param event_timers
+ * Pointer to an array of objects of type *rte_event_timer* structure.
+ * @param nb_event_timers
+ * Number of event timers in the supplied array.
+ *
+ * @return
+ * The number of successfully armed event timers. The return value can be less
+ * than the value of the *nb_timers* parameter. If the return value is less
+ * than *nb_events*, the remaining event timers at the end of *tim*
+ * are not consumed, and the caller has to take care of them, and rte_errno
+ * is set accordingly. Possible errno values include:
+ * - -EINVAL Invalid timer adapter identifier, expiry event queue ID is
+ * invalid, or an expiry event's sched type doesn't match the capabilities of
+ * the destination event queue.
+ * - -EAGAIN Specified timer adapter is not running
+ */
+int rte_event_timer_arm_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ uint16_t nb_event_timers);
+
+/**
+ * Arm a burst of event timers with same expiration timeout tick.
+ *
+ * Provides the same functionality as ``rte_event_timer_arm_burst()``, except
+ * that application can use this API when all the event timers have the
+ * same timeout expiration tick. This specialized function can provide the
+ * additional hint to the adapter implementation and optimize if possible.
+ *
+ * @param adapter
+ * A pointer to an event timer adapter structure.
+ * @param event_timers
+ * Points to an array of objects of type *rte_event_timer* structure.
+ * @param timeout_ticks
+ * The number of ticks in which the timers should expire.
+ * @param nb_event_timers
+ * Number of event timers in the supplied array.
+ *
+ * @return
+ * The number of successfully armed event timers. The return value can be less
+ * than the value of the *nb_timers* parameter. If the return value is less
+ * than *nb_events*, the remaining event timers at the end of *tim*
+ * are not consumed, and the caller has to take care of them, and rte_errno
+ * is set accordingly. Possible errno values include:
+ * - -EINVAL Invalid timer adapter identifier, expiry event queue ID is
+ * invalid, or an expiry event's sched type doesn't match the capabilities of
+ * the destination event queue.
+ * - -EAGAIN Specified event timer adapter is not running
+ */
+int rte_event_timer_arm_tmo_tick_burst(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ const uint64_t timeout_ticks,
+ const uint16_t nb_event_timers);
+
+/**
+ * Cancel a burst of event timer from being scheduled to the event device.
+ *
+ * @param adapter
+ * A pointer to an event timer adapter structure.
+ * @param event_timers
+ * Points to an array of objects of type *rte_event_timer* structure
+ * @param nb_event_timers
+ * Number of event timer instances in the supplied array.
+ *
+ * @return
+ * The number of successfully canceled event timers. The return value can be
+ * less than the value of the *nb_timers* parameter. If the return value is
+ * less than *nb_events*, the remaining event timers at the end of *tim*
+ * are not consumed, and the caller has to take care of them, and rte_errno
+ * is set accordingly. Possible errno values include:
+ * - -EINVAL Invalid timer adapter identifier
+ * - -EAGAIN Specified timer adapter is not running
+ */
+int rte_event_timer_cancel_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ uint16_t nb_event_timers);
+
+#endif /* __RTE_EVENT_TIMER_ADAPTER_H__ */
diff --git a/lib/librte_eventdev/rte_eventdev.h b/lib/librte_eventdev/rte_eventdev.h
index f1949ff..a650f7a 100644
--- a/lib/librte_eventdev/rte_eventdev.h
+++ b/lib/librte_eventdev/rte_eventdev.h
@@ -875,8 +875,8 @@ rte_event_dev_close(uint8_t dev_id);
/**< The event generated from ethdev subsystem */
#define RTE_EVENT_TYPE_CRYPTODEV 0x1
/**< The event generated from crypodev subsystem */
-#define RTE_EVENT_TYPE_TIMERDEV 0x2
-/**< The event generated from timerdev subsystem */
+#define RTE_EVENT_TYPE_TIMER 0x2
+/**< The event generated from event timer adapter */
#define RTE_EVENT_TYPE_CPU 0x3
/**< The event generated from cpu for pipelining.
* Application may use *sub_event_type* to further classify the event
--
2.6.4
Pavan Nikhilesh Bhagavatula
2017-11-29 10:29:01 UTC
Permalink
Post by Jerin Jacob
# versioning export map
EXPORT_MAP := rte_eventdev_version.map
diff --git a/lib/librte_eventdev/rte_event_timer_adapter.h b/lib/librte_eventdev/rte_event_timer_adapter.h
new file mode 100644
index 0000000..14661b5
--- /dev/null
+++ b/lib/librte_eventdev/rte_event_timer_adapter.h
@@ -0,0 +1,427 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
Please add dual licensing here with reference to [1]
Post by Jerin Jacob
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __RTE_EVENT_TIMER_ADAPTER_H__
+#define __RTE_EVENT_TIMER_ADAPTER_H__
+
+/**
+ *
+ * RTE Event Timer Adapter
+ *
+ * TODO: description
Addin the description from the proposed RFC[1]


<snip>
Post by Jerin Jacob
+/**
+ * Event timer state.
+ */
+enum rte_event_timer_state {
+ RTE_EVENT_TIMER_NOT_ARMED = 0,
+ /**< Event timer is in not armed state.*/
+ RTE_EVENT_TIMER_ARMED = 1,
Add a state to indicate TIMER_CANCELLED as per [1]
Post by Jerin Jacob
+ /**< Event timer successfully armed.*/
+ RTE_EVENT_TIMER_ERROR = -1,
+ /**< Generic event timer error.*/
+ RTE_EVENT_TIMER_ERROR_TOOEARLY = -2,
+ /**< Event timer timeout tick is too little to add to the adapter. */
+ RTE_EVENT_TIMER_ERROR_TOOLATE = -3,
+ /**< Event timer timeout tick is greater than the maximum timeout.*/
+};
+
[1] http://dpdk.org/ml/archives/dev/2017-August/072981.html

-Pavan
Erik Gabriel Carrillo
2017-11-28 17:40:06 UTC
Permalink
This commit adds the logic that is shared by all event timer adapter
drivers; the common code handles instance allocation and some
initialization.

Signed-off-by: Erik Gabriel Carrillo <***@intel.com>
---
drivers/event/sw/sw_evdev.c | 16 +
lib/librte_eventdev/Makefile | 2 +
lib/librte_eventdev/rte_event_timer_adapter.c | 414 +++++++++++++++++++++
.../rte_event_timer_adapter_driver.h | 159 ++++++++
lib/librte_eventdev/rte_eventdev.h | 3 +
lib/librte_eventdev/rte_eventdev_pmd.h | 7 +
lib/librte_eventdev/rte_eventdev_version.map | 15 +-
7 files changed, 615 insertions(+), 1 deletion(-)
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter.c
create mode 100644 lib/librte_eventdev/rte_event_timer_adapter_driver.h

diff --git a/drivers/event/sw/sw_evdev.c b/drivers/event/sw/sw_evdev.c
index fd11079..cba8c8c 100644
--- a/drivers/event/sw/sw_evdev.c
+++ b/drivers/event/sw/sw_evdev.c
@@ -435,6 +435,20 @@ sw_eth_rx_adapter_caps_get(const struct rte_eventdev *dev,
return 0;
}

+static int
+sw_timer_adapter_caps_get(const struct rte_eventdev *dev,
+ uint32_t *caps,
+ const struct rte_event_timer_adapter_ops **ops)
+{
+ RTE_SET_USED(dev);
+ *caps = 0;
+
+ /* Use default SW ops */
+ *ops = NULL;
+
+ return 0;
+}
+
static void
sw_info_get(struct rte_eventdev *dev, struct rte_event_dev_info *info)
{
@@ -755,6 +769,8 @@ sw_probe(struct rte_vdev_device *vdev)

.eth_rx_adapter_caps_get = sw_eth_rx_adapter_caps_get,

+ .timer_adapter_caps_get = sw_timer_adapter_caps_get,
+
.xstats_get = sw_xstats_get,
.xstats_get_names = sw_xstats_get_names,
.xstats_get_by_name = sw_xstats_get_by_name,
diff --git a/lib/librte_eventdev/Makefile b/lib/librte_eventdev/Makefile
index 6ef7c1c..ad1a5db 100644
--- a/lib/librte_eventdev/Makefile
+++ b/lib/librte_eventdev/Makefile
@@ -45,6 +45,7 @@ LDLIBS += -lrte_eal -lrte_ring -lrte_ethdev -lrte_hash
SRCS-y += rte_eventdev.c
SRCS-y += rte_event_ring.c
SRCS-y += rte_event_eth_rx_adapter.c
+SRCS-y += rte_event_timer_adapter.c

# export include files
SYMLINK-y-include += rte_eventdev.h
@@ -54,6 +55,7 @@ SYMLINK-y-include += rte_eventdev_pmd_vdev.h
SYMLINK-y-include += rte_event_ring.h
SYMLINK-y-include += rte_event_eth_rx_adapter.h
SYMLINK-y-include += rte_event_timer_adapter.h
+SYMLINK-y-include += rte_event_timer_adapter_driver.h

# versioning export map
EXPORT_MAP := rte_eventdev_version.map
diff --git a/lib/librte_eventdev/rte_event_timer_adapter.c b/lib/librte_eventdev/rte_event_timer_adapter.c
new file mode 100644
index 0000000..7708e2b
--- /dev/null
+++ b/lib/librte_eventdev/rte_event_timer_adapter.c
@@ -0,0 +1,414 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include <rte_memzone.h>
+#include <rte_memory.h>
+#include <rte_dev.h>
+#include <rte_errno.h>
+
+#include "rte_eventdev.h"
+#include "rte_eventdev_pmd.h"
+#include "rte_event_timer_adapter.h"
+#include "rte_event_timer_adapter_driver.h"
+
+#define MAX_EVENT_TIMER_ADAPTERS 64
+#define DATA_MZ_NAME_MAX_LEN 64
+#define DATA_MZ_NAME_FORMAT "rte_event_timer_adapter_data_%d"
+
+static struct rte_event_timer_adapter adapters[MAX_EVENT_TIMER_ADAPTERS];
+
+static inline int
+adapter_valid(const struct rte_event_timer_adapter *adapter)
+{
+ return adapter != NULL && adapter->allocated == 1;
+}
+
+#define ADAPTER_VALID_OR_ERR_RET(adapter, retval) do { \
+ if (!adapter_valid(adapter)) \
+ return retval; \
+} while (0)
+
+#define FUNC_PTR_OR_ERR_RET(func, errval) do { \
+ if ((func) == NULL) \
+ return errval; \
+} while (0)
+
+#define FUNC_PTR_OR_NULL_RET_WITH_ERRNO(func, errval) do { \
+ if ((func) == NULL) { \
+ rte_errno = errval; \
+ return NULL; \
+ } \
+} while (0)
+
+static int
+default_port_conf_cb(uint16_t id, uint8_t event_dev_id, uint8_t *event_port_id,
+ void *conf_arg)
+{
+ struct rte_event_timer_adapter *adapter;
+ struct rte_eventdev *dev;
+ struct rte_event_dev_config dev_conf;
+ struct rte_event_port_conf *port_conf = conf_arg;
+ int started;
+ uint8_t port_id;
+ uint8_t dev_id;
+ int ret;
+
+ RTE_SET_USED(event_dev_id);
+
+ adapter = &adapters[id];
+ dev = &rte_eventdevs[adapter->data->event_dev_id];
+ dev_id = dev->data->dev_id;
+ dev_conf = dev->data->dev_conf;
+
+ started = dev->data->dev_started;
+ if (started)
+ rte_event_dev_stop(dev_id);
+
+ port_id = dev_conf.nb_event_ports;
+ dev_conf.nb_event_ports += 1;
+ ret = rte_event_dev_configure(dev_id, &dev_conf);
+ if (ret < 0) {
+ if (started)
+ rte_event_dev_start(dev_id);
+
+ return ret;
+ }
+
+ ret = rte_event_port_setup(dev_id, port_id, port_conf);
+ if (ret < 0)
+ return ret;
+
+ *event_port_id = port_id;
+
+ if (started)
+ rte_event_dev_start(dev_id);
+
+ return 0;
+}
+
+struct rte_event_timer_adapter *
+rte_event_timer_adapter_create(const struct rte_event_timer_adapter_conf *conf)
+{
+ /* default port conf values */
+ struct rte_event_port_conf port_conf = {
+ .new_event_threshold = 128,
+ .dequeue_depth = 32,
+ .enqueue_depth = 32
+ };
+
+ return rte_event_timer_adapter_create_ext(conf, default_port_conf_cb,
+ &port_conf);
+}
+
+struct rte_event_timer_adapter *
+rte_event_timer_adapter_create_ext(
+ const struct rte_event_timer_adapter_conf *conf,
+ rte_event_timer_adapter_port_conf_cb_t conf_cb,
+ void *conf_arg)
+{
+ uint16_t adapter_id;
+ struct rte_event_timer_adapter *adapter;
+ const struct rte_memzone *mz;
+ char mz_name[DATA_MZ_NAME_MAX_LEN];
+ int n, ret;
+ struct rte_eventdev *dev;
+
+ if (conf == NULL) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ /* Check eventdev ID */
+ if (!rte_event_pmd_is_valid_dev(conf->event_dev_id)) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+ dev = &rte_eventdevs[conf->event_dev_id];
+
+ adapter_id = conf->timer_adapter_id;
+
+ /* Check adapter ID not already allocated */
+ adapter = &adapters[adapter_id];
+ if (adapter->allocated) {
+ rte_errno = -EEXIST;
+ return NULL;
+ }
+
+ /* Create shared data area. */
+ n = snprintf(mz_name, sizeof(mz_name), DATA_MZ_NAME_FORMAT, adapter_id);
+ if (n >= (int)sizeof(mz_name)) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+ mz = rte_memzone_reserve(mz_name,
+ sizeof(struct rte_event_timer_adapter_data),
+ conf->socket_id, 0);
+ if (mz == NULL)
+ /* rte_errno set by rte_memzone_reserve */
+ return NULL;
+
+ adapter->data = mz->addr;
+ memset(adapter->data, 0, sizeof(struct rte_event_timer_adapter_data));
+
+ adapter->data->mz = mz;
+ adapter->data->event_dev_id = conf->event_dev_id;
+ adapter->data->id = adapter_id;
+ adapter->data->socket_id = conf->socket_id;
+ adapter->data->conf = *conf; /* copy conf structure */
+
+ /* Query eventdev PMD for timer adapter capabilities and ops */
+ ret = dev->dev_ops->timer_adapter_caps_get(dev,
+ &adapter->data->caps,
+ &adapter->ops);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ if (!(adapter->data->caps &
+ RTE_EVENT_TIMER_ADAPTER_CAP_INTERNAL_PORT)) {
+ FUNC_PTR_OR_NULL_RET_WITH_ERRNO(conf_cb, -EINVAL);
+ ret = conf_cb(adapter->data->id, adapter->data->event_dev_id,
+ &adapter->data->event_port_id, conf_arg);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+ }
+
+ /* Allow driver to do some setup */
+ FUNC_PTR_OR_NULL_RET_WITH_ERRNO(adapter->ops->init, -ENOTSUP);
+ ret = adapter->ops->init(adapter);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ /* Set fast-path function pointers */
+ adapter->arm_burst = adapter->ops->arm_burst;
+ adapter->arm_tmo_tick_burst = adapter->ops->arm_tmo_tick_burst;
+ adapter->cancel_burst = adapter->ops->cancel_burst;
+
+ adapter->allocated = 1;
+
+ return adapter;
+}
+
+int
+rte_event_timer_adapter_get_info(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer_adapter_info *adapter_info)
+{
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+
+ if (adapter->ops->get_info)
+ /* let driver set values it knows */
+ adapter->ops->get_info(adapter, adapter_info);
+
+ /* Set common values */
+ adapter_info->conf = adapter->data->conf;
+ adapter_info->event_dev_port_id = adapter->data->event_port_id;
+ adapter_info->caps = adapter->data->caps;
+
+ return 0;
+}
+
+int
+rte_event_timer_adapter_start(const struct rte_event_timer_adapter *adapter)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->ops->start, -EINVAL);
+
+ ret = adapter->ops->start(adapter);
+ if (ret < 0)
+ return ret;
+
+ adapter->data->started = 1;
+
+ return 0;
+}
+
+int
+rte_event_timer_adapter_stop(const struct rte_event_timer_adapter *adapter)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->ops->stop, -EINVAL);
+
+ ret = adapter->ops->stop(adapter);
+ if (ret < 0)
+ return ret;
+
+ adapter->data->started = 0;
+
+ return 0;
+}
+
+struct rte_event_timer_adapter *
+rte_event_timer_adapter_lookup(uint16_t adapter_id)
+{
+ char name[DATA_MZ_NAME_MAX_LEN];
+ const struct rte_memzone *mz;
+ struct rte_event_timer_adapter_data *data;
+ struct rte_event_timer_adapter *adapter;
+ int ret;
+ struct rte_eventdev *dev;
+
+ if (adapters[adapter_id].allocated)
+ return &adapters[adapter_id]; /* Adapter is already loaded */
+
+ snprintf(name, DATA_MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, adapter_id);
+ mz = rte_memzone_lookup(name);
+ if (mz == NULL) {
+ rte_errno = -ENOENT;
+ return NULL;
+ }
+
+ data = mz->addr;
+
+ adapter = &adapters[data->id];
+ adapter->data = data;
+
+ dev = &rte_eventdevs[adapter->data->event_dev_id];
+
+ /* Query eventdev PMD for timer adapter capabilities and ops */
+ ret = dev->dev_ops->timer_adapter_caps_get(dev,
+ &adapter->data->caps,
+ &adapter->ops);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ /* Set fast-path function pointers */
+ adapter->arm_burst = adapter->ops->arm_burst;
+ adapter->arm_tmo_tick_burst = adapter->ops->arm_tmo_tick_burst;
+ adapter->cancel_burst = adapter->ops->cancel_burst;
+
+ adapter->allocated = 1;
+
+ return adapter;
+}
+
+int
+rte_event_timer_adapter_free(struct rte_event_timer_adapter *adapter)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->ops->uninit, -EINVAL);
+
+ /* free impl priv data */
+ ret = adapter->ops->uninit(adapter);
+ if (ret < 0)
+ return ret;
+
+ /* free shared data area */
+ ret = rte_memzone_free(adapter->data->mz);
+ if (ret < 0)
+ return ret;
+
+ adapter->data = NULL;
+ adapter->allocated = 0;
+
+ return 0;
+}
+
+int
+rte_event_timer_arm_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ uint16_t nb_event_timers)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->arm_burst, -EINVAL);
+
+ if (!adapter->data->started)
+ return -EAGAIN;
+
+ ret = adapter->arm_burst(adapter, event_timers, nb_event_timers);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int
+rte_event_timer_arm_tmo_tick_burst(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ const uint64_t timeout_ticks,
+ const uint16_t nb_event_timers)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->arm_tmo_tick_burst, -EINVAL);
+
+ if (!adapter->data->started)
+ return -EAGAIN;
+
+ for (int i = 0; i < nb_event_timers; i++)
+ event_timers[i]->timeout_ticks = timeout_ticks;
+
+ ret = adapter->arm_tmo_tick_burst(adapter, event_timers, timeout_ticks,
+ nb_event_timers);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int
+rte_event_timer_cancel_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ uint16_t nb_event_timers)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->cancel_burst, -EINVAL);
+
+ if (!adapter->data->started)
+ return -EAGAIN;
+
+ ret = adapter->cancel_burst(adapter, event_timers, nb_event_timers);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
diff --git a/lib/librte_eventdev/rte_event_timer_adapter_driver.h b/lib/librte_eventdev/rte_event_timer_adapter_driver.h
new file mode 100644
index 0000000..485fad1
--- /dev/null
+++ b/lib/librte_eventdev/rte_event_timer_adapter_driver.h
@@ -0,0 +1,159 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __RTE_EVENT_TIMER_ADAPTER_DRIVER_H__
+#define __RTE_EVENT_TIMER_ADAPTER_DRIVER_H__
+
+/**
+ * @file
+ *
+ * Description
+ *
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "rte_event_timer_adapter.h"
+
+/*
+ * Definitions of functions exported by an event timer adapter implementation
+ * through *rte_event_timer_adapter_ops* structure supplied in the
+ * *rte_event_timer_adapter* structure associated with an event timer adapter.
+ */
+
+typedef int (*rte_event_timer_adapter_init_t)(
+ struct rte_event_timer_adapter *adapter);
+/**< @internal Event timer adapter implementation setup */
+typedef int (*rte_event_timer_adapter_uninit_t)(
+ struct rte_event_timer_adapter *adapter);
+/**< @internal Event timer adapter implementation teardown */
+typedef int (*rte_event_timer_adapter_start_t)(
+ const struct rte_event_timer_adapter *adapter);
+/**< @internal Start running event timer adapter */
+typedef int (*rte_event_timer_adapter_stop_t)(
+ const struct rte_event_timer_adapter *adapter);
+/**< @internal Stop running event timer adapter */
+typedef void (*rte_event_timer_adapter_get_info_t)(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer_adapter_info *adapter_info);
+/**< @internal Get contextual information for event timer adapter */
+typedef int (*rte_event_timer_arm_burst_t)(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ uint16_t nb_tims);
+/**< @internal Enable event timers to enqueue timer events upon expiry */
+typedef int (*rte_event_timer_arm_tmo_tick_burst_t)(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ uint64_t timeout_tick,
+ uint16_t nb_tims);
+/**< @internal Enable event timers with common expiration time */
+typedef int (*rte_event_timer_cancel_burst_t)(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ uint16_t nb_tims);
+/**< @internal Prevent event timers from enqueuing timer events */
+
+/**
+ * @internal Structure containing the functions exported by an event timer
+ * adapter implementation.
+ */
+struct rte_event_timer_adapter_ops {
+ rte_event_timer_adapter_init_t init; /**< Set up adapter */
+ rte_event_timer_adapter_uninit_t uninit;/**< Tear down adapter */
+ rte_event_timer_adapter_start_t start; /**< Start adapter */
+ rte_event_timer_adapter_stop_t stop; /**< Stop adapter */
+ rte_event_timer_adapter_get_info_t get_info;
+ /**< Get info from driver */
+ rte_event_timer_arm_burst_t arm_burst;
+ /**< Arm one or more event timers */
+ rte_event_timer_arm_tmo_tick_burst_t arm_tmo_tick_burst;
+ /**< Arm event timers with same expiration time */
+ rte_event_timer_cancel_burst_t cancel_burst;
+ /**< Cancel one or more event timers */
+};
+
+/**
+ * @internal Adapter data; structure to be placed in shared memory to be
+ * accessible by various processes in a multi-process configuration.
+ */
+struct rte_event_timer_adapter_data {
+ uint8_t id;
+ /**< Event timer adapter ID */
+ uint8_t event_dev_id;
+ /**< Event device ID */
+ uint32_t socket_id;
+ /**< Socket ID where memory is allocated */
+ uint8_t event_port_id;
+ /**< Optional: event port ID used when the inbuilt port is absent */
+ const struct rte_memzone *mz;
+ /**< Event timer adapter memzone pointer */
+ struct rte_event_timer_adapter_conf conf;
+ /**< Configuration used to configure the adapter. */
+ uint32_t caps;
+ /**< Adapter capabilities */
+ void *adapter_priv;
+ /**< Timer adapter private data*/
+
+ RTE_STD_C11
+ uint8_t started : 1;
+ /**< Flag to indicate adapter started. */
+} __rte_cache_aligned;
+
+/**
+ * @internal Data structure associated with each event timer adapter.
+ */
+struct rte_event_timer_adapter {
+ rte_event_timer_arm_burst_t arm_burst;
+ /**< Pointer to driver arm_burst function. */
+ rte_event_timer_arm_tmo_tick_burst_t arm_tmo_tick_burst;
+ /**< Pointer to driver arm_tmo_tick_burst function. */
+ rte_event_timer_cancel_burst_t cancel_burst;
+ /**< Pointer to driver cancel function. */
+
+ struct rte_event_timer_adapter_data *data;
+ /**< Pointer to shared adapter data */
+ const struct rte_event_timer_adapter_ops *ops;
+ /**< Functions exported by adapter driver */
+
+ RTE_STD_C11
+ uint8_t allocated : 1;
+ /**< Flag to indicate that this adapter has been allocated */
+} __rte_cache_aligned;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __RTE_EVENT_TIMER_ADAPTER_DRIVER_H__ */
diff --git a/lib/librte_eventdev/rte_eventdev.h b/lib/librte_eventdev/rte_eventdev.h
index a650f7a..e72da76 100644
--- a/lib/librte_eventdev/rte_eventdev.h
+++ b/lib/librte_eventdev/rte_eventdev.h
@@ -1025,6 +1025,9 @@ struct rte_event {
* @see struct rte_event_eth_rx_adapter_queue_conf::rx_queue_flags
*/

+#define RTE_EVENT_TIMER_ADAPTER_CAP_INTERNAL_PORT (1ULL << 1)
+/**< This flag is set when the timer mechanism is in HW. */
+
/**
* Retrieve the event device's ethdev Rx adapter capabilities for the
* specified ethernet port
diff --git a/lib/librte_eventdev/rte_eventdev_pmd.h b/lib/librte_eventdev/rte_eventdev_pmd.h
index 7a206c5..719fde3 100644
--- a/lib/librte_eventdev/rte_eventdev_pmd.h
+++ b/lib/librte_eventdev/rte_eventdev_pmd.h
@@ -52,6 +52,7 @@ extern "C" {
#include <rte_malloc.h>

#include "rte_eventdev.h"
+#include "rte_event_timer_adapter_driver.h"

/* Logging Macros */
#define RTE_EDEV_LOG_ERR(...) \
@@ -466,6 +467,9 @@ typedef int (*eventdev_eth_rx_adapter_caps_get_t)

struct rte_event_eth_rx_adapter_queue_conf *queue_conf;

+typedef int (*eventdev_timer_adapter_caps_get_t)(const struct rte_eventdev *dev,
+ uint32_t *caps, const struct rte_event_timer_adapter_ops **ops);
+
/**
* Add ethernet Rx queues to event device. This callback is invoked if
* the caps returned from rte_eventdev_eth_rx_adapter_caps_get(, eth_port_id)
@@ -650,6 +654,9 @@ struct rte_eventdev_ops {
/**< Get ethernet Rx stats */
eventdev_eth_rx_adapter_stats_reset eth_rx_adapter_stats_reset;
/**< Reset ethernet Rx stats */
+
+ eventdev_timer_adapter_caps_get_t timer_adapter_caps_get;
+ /**< Get timer adapter capabilities */
};

/**
diff --git a/lib/librte_eventdev/rte_eventdev_version.map b/lib/librte_eventdev/rte_eventdev_version.map
index 108ae61..f56ca0f 100644
--- a/lib/librte_eventdev/rte_eventdev_version.map
+++ b/lib/librte_eventdev/rte_eventdev_version.map
@@ -66,5 +66,18 @@ DPDK_17.11 {
rte_event_eth_rx_adapter_stats_get;
rte_event_eth_rx_adapter_stats_reset;
rte_event_eth_rx_adapter_stop;
-
} DPDK_17.08;
+
+DPDK_18.02 {
+ global:
+
+ rte_event_timer_adapter_create;
+ rte_event_timer_adapter_create_ext;
+ rte_event_timer_adapter_free;
+ rte_event_timer_adapter_get_info;
+ rte_event_timer_adapter_start;
+ rte_event_timer_adapter_stop;
+ rte_event_timer_arm_burst;
+ rte_event_timer_arm_tmo_tick_burst;
+ rte_event_timer_cancel_burst;
+} DPDK_17.11;
--
2.6.4
Pavan Nikhilesh Bhagavatula
2017-11-29 05:19:25 UTC
Permalink
Hi Gabriel,

Comments inline

On Tue, Nov 28, 2017 at 11:40:06AM -0600, Erik Gabriel Carrillo wrote:
<snip>
Post by Erik Gabriel Carrillo
+struct rte_event_timer_adapter *
+rte_event_timer_adapter_create(const struct rte_event_timer_adapter_conf *conf)
+{
+ /* default port conf values */
+ struct rte_event_port_conf port_conf = {
+ .new_event_threshold = 128,
+ .dequeue_depth = 32,
+ .enqueue_depth = 32
+ };
Instead of harcoding get the port conf values from
rte_event_port_default_conf_get().
Post by Erik Gabriel Carrillo
+
+ return rte_event_timer_adapter_create_ext(conf, default_port_conf_cb,
+ &port_conf);
+}
+
+struct rte_event_timer_adapter *
+rte_event_timer_adapter_create_ext(
+ const struct rte_event_timer_adapter_conf *conf,
+ rte_event_timer_adapter_port_conf_cb_t conf_cb,
+ void *conf_arg)
+{
+ uint16_t adapter_id;
+ struct rte_event_timer_adapter *adapter;
+ const struct rte_memzone *mz;
+ char mz_name[DATA_MZ_NAME_MAX_LEN];
+ int n, ret;
+ struct rte_eventdev *dev;
+
+ if (conf == NULL) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ /* Check eventdev ID */
+ if (!rte_event_pmd_is_valid_dev(conf->event_dev_id)) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+ dev = &rte_eventdevs[conf->event_dev_id];
+
+ adapter_id = conf->timer_adapter_id;
+
+ /* Check adapter ID not already allocated */
+ adapter = &adapters[adapter_id];
+ if (adapter->allocated) {
+ rte_errno = -EEXIST;
+ return NULL;
+ }
+
+ /* Create shared data area. */
+ n = snprintf(mz_name, sizeof(mz_name), DATA_MZ_NAME_FORMAT, adapter_id);
+ if (n >= (int)sizeof(mz_name)) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+ mz = rte_memzone_reserve(mz_name,
+ sizeof(struct rte_event_timer_adapter_data),
+ conf->socket_id, 0);
+ if (mz == NULL)
+ /* rte_errno set by rte_memzone_reserve */
+ return NULL;
+
+ adapter->data = mz->addr;
+ memset(adapter->data, 0, sizeof(struct rte_event_timer_adapter_data));
+
+ adapter->data->mz = mz;
+ adapter->data->event_dev_id = conf->event_dev_id;
+ adapter->data->id = adapter_id;
+ adapter->data->socket_id = conf->socket_id;
+ adapter->data->conf = *conf; /* copy conf structure */
+
+ /* Query eventdev PMD for timer adapter capabilities and ops */
+ ret = dev->dev_ops->timer_adapter_caps_get(dev,
+ &adapter->data->caps,
+ &adapter->ops);
The underlying driver needs info about the adapter flags i.e.
RTE_EVENT_TIMER_ADAPTER_F_ADJUST_RES and RTE_EVENT_TIMER_ADAPTER_F_SP_PUT so we
need to pass those too conf->flags.
Post by Erik Gabriel Carrillo
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ if (!(adapter->data->caps &
+ RTE_EVENT_TIMER_ADAPTER_CAP_INTERNAL_PORT)) {
+ FUNC_PTR_OR_NULL_RET_WITH_ERRNO(conf_cb, -EINVAL);
+ ret = conf_cb(adapter->data->id, adapter->data->event_dev_id,
+ &adapter->data->event_port_id, conf_arg);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+ }
+
+ /* Allow driver to do some setup */
+ FUNC_PTR_OR_NULL_RET_WITH_ERRNO(adapter->ops->init, -ENOTSUP);
+ ret = adapter->ops->init(adapter);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ /* Set fast-path function pointers */
+ adapter->arm_burst = adapter->ops->arm_burst;
+ adapter->arm_tmo_tick_burst = adapter->ops->arm_tmo_tick_burst;
+ adapter->cancel_burst = adapter->ops->cancel_burst;
+
+ adapter->allocated = 1;
+
+ return adapter;
+}
+
+struct rte_event_timer_adapter *
+rte_event_timer_adapter_lookup(uint16_t adapter_id)
+{
+ char name[DATA_MZ_NAME_MAX_LEN];
+ const struct rte_memzone *mz;
+ struct rte_event_timer_adapter_data *data;
+ struct rte_event_timer_adapter *adapter;
+ int ret;
+ struct rte_eventdev *dev;
+
+ if (adapters[adapter_id].allocated)
+ return &adapters[adapter_id]; /* Adapter is already loaded */
+
+ snprintf(name, DATA_MZ_NAME_MAX_LEN, DATA_MZ_NAME_FORMAT, adapter_id);
+ mz = rte_memzone_lookup(name);
+ if (mz == NULL) {
+ rte_errno = -ENOENT;
+ return NULL;
+ }
+
+ data = mz->addr;
+
+ adapter = &adapters[data->id];
+ adapter->data = data;
+
+ dev = &rte_eventdevs[adapter->data->event_dev_id];
+
+ /* Query eventdev PMD for timer adapter capabilities and ops */
+ ret = dev->dev_ops->timer_adapter_caps_get(dev,
+ &adapter->data->caps,
+ &adapter->ops);
Same as above.
Post by Erik Gabriel Carrillo
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ /* Set fast-path function pointers */
+ adapter->arm_burst = adapter->ops->arm_burst;
+ adapter->arm_tmo_tick_burst = adapter->ops->arm_tmo_tick_burst;
+ adapter->cancel_burst = adapter->ops->cancel_burst;
+
+ adapter->allocated = 1;
+
+ return adapter;
+}
+
<snip>
Post by Erik Gabriel Carrillo
+int
+rte_event_timer_arm_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ uint16_t nb_event_timers)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->arm_burst, -EINVAL);
+
+ if (!adapter->data->started)
+ return -EAGAIN;
These checks are datapath heavy so enable them under debug compilation.
Post by Erik Gabriel Carrillo
+
+ ret = adapter->arm_burst(adapter, event_timers, nb_event_timers);
+ if (ret < 0)
+ return ret;
+
+ return 0;
when burst is called we need to return the number of timers successfully set
and free the failures. Return the result directly.
Post by Erik Gabriel Carrillo
+}
+
+int
+rte_event_timer_arm_tmo_tick_burst(
+ const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ const uint64_t timeout_ticks,
+ const uint16_t nb_event_timers)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->arm_tmo_tick_burst, -EINVAL);
+
+ if (!adapter->data->started)
+ return -EAGAIN;
+
+ for (int i = 0; i < nb_event_timers; i++)
+ event_timers[i]->timeout_ticks = timeout_ticks;
+
+ ret = adapter->arm_tmo_tick_burst(adapter, event_timers, timeout_ticks,
+ nb_event_timers);
+ if (ret < 0)
+ return ret;
+
+ return 0;
Same as above.
Post by Erik Gabriel Carrillo
+}
+
+int
+rte_event_timer_cancel_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **event_timers,
+ uint16_t nb_event_timers)
+{
+ int ret;
+
+ ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+ FUNC_PTR_OR_ERR_RET(adapter->cancel_burst, -EINVAL);
+
+ if (!adapter->data->started)
+ return -EAGAIN;
+
+ ret = adapter->cancel_burst(adapter, event_timers, nb_event_timers);
+ if (ret < 0)
+ return ret;
+
+ return 0;
Same as above.
Post by Erik Gabriel Carrillo
+}
diff --git a/lib/librte_eventdev/rte_event_timer_adapter_driver.h b/lib/librte_eventdev/rte_event_timer_adapter_driver.h
Consider naming this _pmd.h for consistency
Post by Erik Gabriel Carrillo
new file mode 100644
index 0000000..485fad1
--- /dev/null
+++ b/lib/librte_eventdev/rte_event_timer_adapter_driver.h
@@ -0,0 +1,159 @@
Regards,
Pavan.
Carrillo, Erik G
2017-11-30 20:59:19 UTC
Permalink
Hi Pavan,

Thanks for the review; I'm working on addressing the comments and have the following question (inline):

<... snipped ...>
Post by Pavan Nikhilesh Bhagavatula
Post by Erik Gabriel Carrillo
+ adapter->data->mz = mz;
+ adapter->data->event_dev_id = conf->event_dev_id;
+ adapter->data->id = adapter_id;
+ adapter->data->socket_id = conf->socket_id;
+ adapter->data->conf = *conf; /* copy conf structure */
+
+ /* Query eventdev PMD for timer adapter capabilities and ops */
+ ret = dev->dev_ops->timer_adapter_caps_get(dev,
+ &adapter->data->caps,
+ &adapter->ops);
The underlying driver needs info about the adapter flags i.e.
RTE_EVENT_TIMER_ADAPTER_F_ADJUST_RES and
RTE_EVENT_TIMER_ADAPTER_F_SP_PUT so we need to pass those too conf-
Post by Erik Gabriel Carrillo
flags.
By "underlying driver", are you referring to the eventdev PMD, or the event timer adapter "driver" (i.e., the set of ops functions)?

If the latter, the adapter "driver" will have a chance to inspect the flags when adapter->ops->init() is called below, since it can look at the flags through the adapter arg.

If the former, will the eventdev PMD consider the flags when deciding whether or not to provide an ops definition in the timer_adapter_caps_get() call?
Post by Pavan Nikhilesh Bhagavatula
Post by Erik Gabriel Carrillo
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ if (!(adapter->data->caps &
+ RTE_EVENT_TIMER_ADAPTER_CAP_INTERNAL_PORT)) {
+ FUNC_PTR_OR_NULL_RET_WITH_ERRNO(conf_cb, -EINVAL);
+ ret = conf_cb(adapter->data->id, adapter->data-
event_dev_id,
+ &adapter->data->event_port_id, conf_arg);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+ }
+
+ /* Allow driver to do some setup */
+ FUNC_PTR_OR_NULL_RET_WITH_ERRNO(adapter->ops->init, -
ENOTSUP);
Post by Erik Gabriel Carrillo
+ ret = adapter->ops->init(adapter);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ /* Set fast-path function pointers */
+ adapter->arm_burst = adapter->ops->arm_burst;
+ adapter->arm_tmo_tick_burst = adapter->ops-
arm_tmo_tick_burst;
+ adapter->cancel_burst = adapter->ops->cancel_burst;
+
+ adapter->allocated = 1;
+
+ return adapter;
+}
<... snipped ...>
Pavan Nikhilesh Bhagavatula
2017-12-01 05:13:15 UTC
Permalink
Post by Carrillo, Erik G
Hi Pavan,
<... snipped ...>
Post by Pavan Nikhilesh Bhagavatula
Post by Erik Gabriel Carrillo
+ adapter->data->mz = mz;
+ adapter->data->event_dev_id = conf->event_dev_id;
+ adapter->data->id = adapter_id;
+ adapter->data->socket_id = conf->socket_id;
+ adapter->data->conf = *conf; /* copy conf structure */
+
+ /* Query eventdev PMD for timer adapter capabilities and ops */
+ ret = dev->dev_ops->timer_adapter_caps_get(dev,
+ &adapter->data->caps,
+ &adapter->ops);
The underlying driver needs info about the adapter flags i.e.
RTE_EVENT_TIMER_ADAPTER_F_ADJUST_RES and
RTE_EVENT_TIMER_ADAPTER_F_SP_PUT so we need to pass those too conf-
Post by Erik Gabriel Carrillo
flags.
By "underlying driver", are you referring to the eventdev PMD, or the event timer adapter "driver" (i.e., the set of ops functions)?
If the latter, the adapter "driver" will have a chance to inspect the flags when adapter->ops->init() is called below, since it can look at the flags through the adapter arg.
I was refering to the timer driver, the presence of flag RTE_EVENT_TIMER_ADAPTER_F_SP_PUT
would suggest the driver to use a multi thread unsafe arm/cancel data path API
and it would set a different function pointers to adapter->arm_burst etc.

I dont think in the current scheme this is possible. Currently, if we see mempool it inspects
flags before setting ops.

Hope this clears things up.

-Pavan
Post by Carrillo, Erik G
If the former, will the eventdev PMD consider the flags when deciding whether or not to provide an ops definition in the timer_adapter_caps_get() call?
Post by Pavan Nikhilesh Bhagavatula
Post by Erik Gabriel Carrillo
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ if (!(adapter->data->caps &
+ RTE_EVENT_TIMER_ADAPTER_CAP_INTERNAL_PORT)) {
+ FUNC_PTR_OR_NULL_RET_WITH_ERRNO(conf_cb, -EINVAL);
+ ret = conf_cb(adapter->data->id, adapter->data-
event_dev_id,
+ &adapter->data->event_port_id, conf_arg);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+ }
+
+ /* Allow driver to do some setup */
+ FUNC_PTR_OR_NULL_RET_WITH_ERRNO(adapter->ops->init, -
ENOTSUP);
Post by Erik Gabriel Carrillo
+ ret = adapter->ops->init(adapter);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ /* Set fast-path function pointers */
+ adapter->arm_burst = adapter->ops->arm_burst;
+ adapter->arm_tmo_tick_burst = adapter->ops-
arm_tmo_tick_burst;
+ adapter->cancel_burst = adapter->ops->cancel_burst;
+
+ adapter->allocated = 1;
+
+ return adapter;
+}
<... snipped ...>
Carrillo, Erik G
2017-12-01 20:19:43 UTC
Permalink
-----Original Message-----
From: Pavan Nikhilesh Bhagavatula
Sent: Thursday, November 30, 2017 11:13 PM
Subject: Re: [RFC PATCH v4 2/4] eventtimer: add common code
Post by Carrillo, Erik G
Hi Pavan,
Thanks for the review; I'm working on addressing the comments and have
<... snipped ...>
Post by Pavan Nikhilesh Bhagavatula
Post by Erik Gabriel Carrillo
+ adapter->data->mz = mz;
+ adapter->data->event_dev_id = conf->event_dev_id;
+ adapter->data->id = adapter_id;
+ adapter->data->socket_id = conf->socket_id;
+ adapter->data->conf = *conf; /* copy conf structure */
+
+ /* Query eventdev PMD for timer adapter capabilities and ops */
+ ret = dev->dev_ops->timer_adapter_caps_get(dev,
+ &adapter->data->caps,
+ &adapter->ops);
The underlying driver needs info about the adapter flags i.e.
RTE_EVENT_TIMER_ADAPTER_F_ADJUST_RES and
RTE_EVENT_TIMER_ADAPTER_F_SP_PUT so we need to pass those too
conf-
Post by Carrillo, Erik G
Post by Pavan Nikhilesh Bhagavatula
Post by Erik Gabriel Carrillo
flags.
By "underlying driver", are you referring to the eventdev PMD, or the
event timer adapter "driver" (i.e., the set of ops functions)?
Post by Carrillo, Erik G
If the latter, the adapter "driver" will have a chance to inspect the flags
when adapter->ops->init() is called below, since it can look at the flags
through the adapter arg.
I was refering to the timer driver, the presence of flag
RTE_EVENT_TIMER_ADAPTER_F_SP_PUT would suggest the driver to use a
multi thread unsafe arm/cancel data path API and it would set a different
function pointers to adapter->arm_burst etc.
I dont think in the current scheme this is possible. Currently, if we see
mempool it inspects flags before setting ops.
Hope this clears things up.
-Pavan
Yes, I see your point now. I agree that it would be useful to allow different ops structures to be selected based on the flags that are set in addition to being able to inspect the flags within the ops functions themselves. I have made the change in the follow-up patch series.

Thanks,
Gabriel
Post by Carrillo, Erik G
If the former, will the eventdev PMD consider the flags when deciding
whether or not to provide an ops definition in the timer_adapter_caps_get()
call?
Post by Carrillo, Erik G
Post by Pavan Nikhilesh Bhagavatula
Post by Erik Gabriel Carrillo
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ if (!(adapter->data->caps &
+ RTE_EVENT_TIMER_ADAPTER_CAP_INTERNAL_PORT)) {
+ FUNC_PTR_OR_NULL_RET_WITH_ERRNO(conf_cb, -EINVAL);
+ ret = conf_cb(adapter->data->id, adapter->data-
event_dev_id,
+ &adapter->data->event_port_id, conf_arg);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+ }
+
+ /* Allow driver to do some setup */
+ FUNC_PTR_OR_NULL_RET_WITH_ERRNO(adapter->ops->init, -
ENOTSUP);
Post by Erik Gabriel Carrillo
+ ret = adapter->ops->init(adapter);
+ if (ret < 0) {
+ rte_errno = -EINVAL;
+ return NULL;
+ }
+
+ /* Set fast-path function pointers */
+ adapter->arm_burst = adapter->ops->arm_burst;
+ adapter->arm_tmo_tick_burst = adapter->ops-
arm_tmo_tick_burst;
+ adapter->cancel_burst = adapter->ops->cancel_burst;
+
+ adapter->allocated = 1;
+
+ return adapter;
+}
<... snipped ...>
Erik Gabriel Carrillo
2017-11-28 17:40:07 UTC
Permalink
If an eventdev PMD does not wish to provide event timer adapter ops
definitions, the library will fall back to a default software
implementation whose entry points are added by this commit.

Signed-off-by: Erik Gabriel Carrillo <***@intel.com>
---
lib/librte_eventdev/Makefile | 1 +
lib/librte_eventdev/rte_event_timer_adapter.c | 14 +++
lib/librte_eventdev/sw_event_timer_adapter.c | 123 ++++++++++++++++++++++++++
3 files changed, 138 insertions(+)
create mode 100644 lib/librte_eventdev/sw_event_timer_adapter.c

diff --git a/lib/librte_eventdev/Makefile b/lib/librte_eventdev/Makefile
index ad1a5db..d5c7cc7 100644
--- a/lib/librte_eventdev/Makefile
+++ b/lib/librte_eventdev/Makefile
@@ -46,6 +46,7 @@ SRCS-y += rte_eventdev.c
SRCS-y += rte_event_ring.c
SRCS-y += rte_event_eth_rx_adapter.c
SRCS-y += rte_event_timer_adapter.c
+SRCS-y += sw_event_timer_adapter.c

# export include files
SYMLINK-y-include += rte_eventdev.h
diff --git a/lib/librte_eventdev/rte_event_timer_adapter.c b/lib/librte_eventdev/rte_event_timer_adapter.c
index 7708e2b..09dcf0f 100644
--- a/lib/librte_eventdev/rte_event_timer_adapter.c
+++ b/lib/librte_eventdev/rte_event_timer_adapter.c
@@ -48,6 +48,8 @@

static struct rte_event_timer_adapter adapters[MAX_EVENT_TIMER_ADAPTERS];

+extern const struct rte_event_timer_adapter_ops sw_event_adapter_timer_ops;
+
static inline int
adapter_valid(const struct rte_event_timer_adapter *adapter)
{
@@ -207,6 +209,12 @@ rte_event_timer_adapter_create_ext(
}
}

+ /* If eventdev PMD did not provide ops, use default software
+ * implementation.
+ */
+ if (adapter->ops == NULL)
+ adapter->ops = &sw_event_adapter_timer_ops;
+
/* Allow driver to do some setup */
FUNC_PTR_OR_NULL_RET_WITH_ERRNO(adapter->ops->init, -ENOTSUP);
ret = adapter->ops->init(adapter);
@@ -313,6 +321,12 @@ rte_event_timer_adapter_lookup(uint16_t adapter_id)
return NULL;
}

+ /* If eventdev PMD did not provide ops, use default software
+ * implementation.
+ */
+ if (adapter->ops == NULL)
+ adapter->ops = &sw_event_adapter_timer_ops;
+
/* Set fast-path function pointers */
adapter->arm_burst = adapter->ops->arm_burst;
adapter->arm_tmo_tick_burst = adapter->ops->arm_tmo_tick_burst;
diff --git a/lib/librte_eventdev/sw_event_timer_adapter.c b/lib/librte_eventdev/sw_event_timer_adapter.c
new file mode 100644
index 0000000..43fdad2
--- /dev/null
+++ b/lib/librte_eventdev/sw_event_timer_adapter.c
@@ -0,0 +1,123 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "rte_event_timer_adapter.h"
+#include "rte_event_timer_adapter_driver.h"
+
+static int
+sw_event_timer_adapter_init(struct rte_event_timer_adapter *adapter)
+{
+ RTE_SET_USED(adapter);
+
+ return 0;
+}
+
+static int
+sw_event_timer_adapter_uninit(struct rte_event_timer_adapter *adapter)
+{
+ RTE_SET_USED(adapter);
+
+ return 0;
+}
+
+static int
+sw_event_timer_adapter_start(const struct rte_event_timer_adapter *adapter)
+{
+ RTE_SET_USED(adapter);
+
+ return 0;
+}
+
+static int
+sw_event_timer_adapter_stop(const struct rte_event_timer_adapter *adapter)
+{
+ RTE_SET_USED(adapter);
+
+ return 0;
+}
+
+static void
+sw_event_timer_adapter_get_info(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer_adapter_info *adapter_info)
+{
+ RTE_SET_USED(adapter);
+ RTE_SET_USED(adapter_info);
+}
+
+static int
+sw_event_timer_arm_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **evtims,
+ uint16_t nb_evtims)
+{
+ RTE_SET_USED(adapter);
+ RTE_SET_USED(evtims);
+ RTE_SET_USED(nb_evtims);
+
+ return 0;
+}
+
+static int
+sw_event_timer_cancel_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **evtims,
+ uint16_t nb_evtims)
+{
+ RTE_SET_USED(adapter);
+ RTE_SET_USED(evtims);
+ RTE_SET_USED(nb_evtims);
+
+ return 0;
+}
+
+static int
+sw_event_timer_arm_tmo_tick_burst(const struct rte_event_timer_adapter *adapter,
+ struct rte_event_timer **tims,
+ uint64_t timeout_tick,
+ uint16_t nb_tims)
+{
+ RTE_SET_USED(adapter);
+ RTE_SET_USED(tims);
+ RTE_SET_USED(timeout_tick);
+ RTE_SET_USED(nb_tims);
+
+ return 0;
+}
+
+const struct rte_event_timer_adapter_ops sw_event_adapter_timer_ops = {
+ .init = sw_event_timer_adapter_init,
+ .uninit = sw_event_timer_adapter_uninit,
+ .start = sw_event_timer_adapter_start,
+ .stop = sw_event_timer_adapter_stop,
+ .get_info = sw_event_timer_adapter_get_info,
+ .arm_burst = sw_event_timer_arm_burst,
+ .arm_tmo_tick_burst = sw_event_timer_arm_tmo_tick_burst,
+ .cancel_burst = sw_event_timer_cancel_burst,
+};
--
2.6.4
Pavan Nikhilesh Bhagavatula
2017-11-29 10:34:47 UTC
Permalink
Post by Erik Gabriel Carrillo
If an eventdev PMD does not wish to provide event timer adapter ops
definitions, the library will fall back to a default software
implementation whose entry points are added by this commit.
I do agree with having sw driver in lib/librte_eventdev as it doesn't fit
anywhere else, but it seems out of place maybe it would be better to merged
it with rte_event_timer_adapter_driver.h or in rte_event_timer_adapter.c as
done by eth_rx_adapter.

Thoughts?
-Pavan
Post by Erik Gabriel Carrillo
---
lib/librte_eventdev/Makefile | 1 +
lib/librte_eventdev/rte_event_timer_adapter.c | 14 +++
lib/librte_eventdev/sw_event_timer_adapter.c | 123 ++++++++++++++++++++++++++
3 files changed, 138 insertions(+)
create mode 100644 lib/librte_eventdev/sw_event_timer_adapter.c
Carrillo, Erik G
2017-11-30 23:56:20 UTC
Permalink
Hi Pavan,
-----Original Message-----
From: Pavan Nikhilesh Bhagavatula
Sent: Wednesday, November 29, 2017 4:35 AM
Subject: Re: [RFC PATCH v4 3/4] eventtimer: add default software
implementation stub
Post by Erik Gabriel Carrillo
If an eventdev PMD does not wish to provide event timer adapter ops
definitions, the library will fall back to a default software
implementation whose entry points are added by this commit.
I do agree with having sw driver in lib/librte_eventdev as it doesn't fit
anywhere else, but it seems out of place maybe it would be better to
merged it with rte_event_timer_adapter_driver.h or in
rte_event_timer_adapter.c as done by eth_rx_adapter.
Thoughts?
I had also considered merging the SW driver with rte_event_timer_adapter.c but then thought it would be cleaner to separate it into its own module.

But I'll make the change so that it's more like the eth rx adapter, and if the file gets too messy when we flesh out the SW driver, we can break it back out.

Thanks,
Gabriel
-Pavan
Post by Erik Gabriel Carrillo
---
lib/librte_eventdev/Makefile | 1 +
lib/librte_eventdev/rte_event_timer_adapter.c | 14 +++
lib/librte_eventdev/sw_event_timer_adapter.c | 123
++++++++++++++++++++++++++
3 files changed, 138 insertions(+)
create mode 100644 lib/librte_eventdev/sw_event_timer_adapter.c
Pavan Nikhilesh Bhagavatula
2017-12-01 05:15:43 UTC
Permalink
Post by Carrillo, Erik G
Hi Pavan,
-----Original Message-----
From: Pavan Nikhilesh Bhagavatula
Sent: Wednesday, November 29, 2017 4:35 AM
Subject: Re: [RFC PATCH v4 3/4] eventtimer: add default software
implementation stub
Post by Erik Gabriel Carrillo
If an eventdev PMD does not wish to provide event timer adapter ops
definitions, the library will fall back to a default software
implementation whose entry points are added by this commit.
I do agree with having sw driver in lib/librte_eventdev as it doesn't fit
anywhere else, but it seems out of place maybe it would be better to
merged it with rte_event_timer_adapter_driver.h or in
rte_event_timer_adapter.c as done by eth_rx_adapter.
Thoughts?
I had also considered merging the SW driver with rte_event_timer_adapter.c but then thought it would be cleaner to separate it into its own module.
But I'll make the change so that it's more like the eth rx adapter, and if the file gets too messy when we flesh out the SW driver, we can break it back out.
Sounds good to me.

Cheers,
Pavan
Post by Carrillo, Erik G
Thanks,
Gabriel
-Pavan
Post by Erik Gabriel Carrillo
---
lib/librte_eventdev/Makefile | 1 +
lib/librte_eventdev/rte_event_timer_adapter.c | 14 +++
lib/librte_eventdev/sw_event_timer_adapter.c | 123
++++++++++++++++++++++++++
3 files changed, 138 insertions(+)
create mode 100644 lib/librte_eventdev/sw_event_timer_adapter.c
Erik Gabriel Carrillo
2017-11-28 17:40:08 UTC
Permalink
This commit adds enough test infrastructure to exercise the
allocation/deallocation routines of the event timer adapter library
minimally.

Signed-off-by: Erik Gabriel Carrillo <***@intel.com>
---
test/test/Makefile | 1 +
test/test/test_event_timer_adapter.c | 249 +++++++++++++++++++++++++++++++++++
2 files changed, 250 insertions(+)
create mode 100644 test/test/test_event_timer_adapter.c

diff --git a/test/test/Makefile b/test/test/Makefile
index bb54c98..9448aef 100644
--- a/test/test/Makefile
+++ b/test/test/Makefile
@@ -210,6 +210,7 @@ ifeq ($(CONFIG_RTE_LIBRTE_EVENTDEV),y)
SRCS-y += test_eventdev.c
SRCS-y += test_event_ring.c
SRCS-y += test_event_eth_rx_adapter.c
+SRCS-y += test_event_timer_adapter.c
SRCS-$(CONFIG_RTE_LIBRTE_PMD_SW_EVENTDEV) += test_eventdev_sw.c
SRCS-$(CONFIG_RTE_LIBRTE_PMD_OCTEONTX_SSOVF) += test_eventdev_octeontx.c
endif
diff --git a/test/test/test_event_timer_adapter.c b/test/test/test_event_timer_adapter.c
new file mode 100644
index 0000000..d0ea066
--- /dev/null
+++ b/test/test/test_event_timer_adapter.c
@@ -0,0 +1,249 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2017 Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include <rte_eventdev.h>
+#include <rte_dev.h>
+#include <rte_bus_vdev.h>
+#include <rte_event_timer_adapter.h>
+#include <rte_mempool.h>
+#include <rte_errno.h>
+#include <rte_service_component.h>
+
+#include "test.h"
+
+/* Example from RFC */
+#define NB_TEST_EVENT_TIMERS 40000
+
+static int evdev;
+//struct rte_event_timer_adapter *g_adapter;
+struct rte_event_timer *g_evtimer;
+struct rte_mempool *g_event_timer_pool;
+
+static inline void
+devconf_set_default_sane_values(struct rte_event_dev_config *dev_conf,
+ struct rte_event_dev_info *info)
+{
+ memset(dev_conf, 0, sizeof(struct rte_event_dev_config));
+ dev_conf->dequeue_timeout_ns = info->min_dequeue_timeout_ns;
+ /* Leave a port for the adapter to allocate */
+ dev_conf->nb_event_ports = info->max_event_ports - 1;
+ dev_conf->nb_event_queues = info->max_event_queues;
+ dev_conf->nb_event_queue_flows = info->max_event_queue_flows;
+ dev_conf->nb_event_port_dequeue_depth =
+ info->max_event_port_dequeue_depth;
+ dev_conf->nb_event_port_enqueue_depth =
+ info->max_event_port_enqueue_depth;
+ dev_conf->nb_event_port_enqueue_depth =
+ info->max_event_port_enqueue_depth;
+ dev_conf->nb_events_limit =
+ info->max_num_events;
+}
+
+static int
+configure_event_dev(void)
+{
+ struct rte_event_dev_config devconf;
+ int ret;
+ const char *eventdev_name = "event_sw0";
+ struct rte_event_dev_info info;
+ int i;
+
+ evdev = rte_event_dev_get_dev_id(eventdev_name);
+ if (evdev < 0) {
+ if (rte_vdev_init(eventdev_name, NULL) < 0) {
+ printf("Error creating eventdev\n");
+ return TEST_FAILED;
+ }
+ evdev = rte_event_dev_get_dev_id(eventdev_name);
+ if (evdev < 0) {
+ printf("Error finding newly created eventdev\n");
+ return TEST_FAILED;
+ }
+ }
+
+ ret = rte_event_dev_info_get(evdev, &info);
+ TEST_ASSERT_SUCCESS(ret, "Failed to get event dev info");
+
+ devconf_set_default_sane_values(&devconf, &info);
+
+ ret = rte_event_dev_configure(evdev, &devconf);
+ TEST_ASSERT_SUCCESS(ret, "Failed to configure eventdev");
+
+ /* Map the event_sw0 service to a service core */
+ ret = rte_service_start_with_defaults();
+ TEST_ASSERT_SUCCESS(ret, "Failed to start sw_evdev service");
+
+ /* Set up event queues */
+ uint32_t queue_count;
+ TEST_ASSERT_SUCCESS(rte_event_dev_attr_get(evdev,
+ RTE_EVENT_DEV_ATTR_QUEUE_COUNT, &queue_count),
+ "Queue count get failed");
+
+ for (i = 0; i < (int)queue_count; i++) {
+ ret = rte_event_queue_setup(evdev, i, NULL);
+ TEST_ASSERT_SUCCESS(ret, "Failed to setup queue=%d", i);
+ }
+
+ /* Set up event ports */
+ uint32_t port_count;
+ TEST_ASSERT_SUCCESS(rte_event_dev_attr_get(evdev,
+ RTE_EVENT_DEV_ATTR_PORT_COUNT,
+ &port_count), "Port count get failed");
+
+ for (i = 0; i < (int)port_count; i++) {
+ ret = rte_event_port_setup(evdev, i, NULL);
+ TEST_ASSERT_SUCCESS(ret, "Failed to setup port=%d", i);
+ /* Link each queues to all ports */
+ ret = rte_event_port_link(evdev, i, NULL, NULL, 0);
+ TEST_ASSERT(ret >= 0, "Failed to link all queues port=%d", i);
+ }
+
+ /* Start the event device */
+ ret = rte_event_dev_start(evdev);
+ TEST_ASSERT_SUCCESS(ret, "Failed to start device");
+
+ return TEST_SUCCESS;
+}
+
+static int
+testsuite_setup(void)
+{
+ int ret;
+
+ /* Setup and start event device. */
+ ret = configure_event_dev();
+ if (ret) {
+ printf("Failed to configure event dev\n");
+ return TEST_FAILED;
+ }
+
+ /* Create a mempool of event timers. */
+ g_event_timer_pool = rte_mempool_create("event_timer_mempool",
+ NB_TEST_EVENT_TIMERS,
+ sizeof(struct rte_event_timer),
+ 0,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ rte_socket_id(),
+ 0);
+ if (g_event_timer_pool == NULL) {
+ /* Failed to create event timer mempool. */
+ printf("Failed to configure event timer mempool: %s\n",
+ rte_strerror(rte_errno));
+ return TEST_FAILED;
+ }
+
+ return TEST_SUCCESS;
+}
+
+static void
+testsuite_teardown(void)
+{
+ /* TODO: tear down adapter and evdev */
+
+ rte_mempool_free(g_event_timer_pool);
+}
+
+#define NSECPERSEC 1E9 // No of ns for 1 sec
+
+static int
+adapter_create_free(void)
+{
+ int ret;
+ int adapter_id = 0;
+ struct rte_event_timer_adapter *adapter;
+
+ struct rte_event_timer_adapter_conf conf = {
+ .event_dev_id = evdev,
+ .timer_adapter_id = adapter_id,
+ .clk_src = RTE_EVENT_TIMER_ADAPTER_CPU_CLK,
+ .timer_tick_ns = NSECPERSEC / 10, // 100 milliseconds
+ .max_tmo_ns = 180 * NSECPERSEC, // 2 minutes
+ .nb_timers = NB_TEST_EVENT_TIMERS,
+ .flags = 0,
+ };
+
+ adapter = rte_event_timer_adapter_create(&conf);
+ if (adapter == NULL) {
+ printf("Failed to create adapter\n");
+ return TEST_FAILED;
+ }
+
+ /* Move to separate tests later; just verify plugin connections for
+ * now
+ */
+
+ struct rte_event_timer_adapter_info adapter_info;
+ ret = rte_event_timer_adapter_get_info(adapter, &adapter_info);
+ if (ret < 0)
+ return TEST_FAILED;
+
+ ret = rte_event_timer_adapter_start(adapter);
+ if (ret < 0)
+ return TEST_FAILED;
+
+ ret = rte_event_timer_adapter_stop(adapter);
+ if (ret < 0)
+ return TEST_FAILED;
+
+ ret = rte_event_timer_adapter_free(adapter);
+ if (ret) {
+ printf("Failed to free adapter\n");
+ return TEST_FAILED;
+ }
+
+ return TEST_SUCCESS;
+}
+
+static struct unit_test_suite adapter_tests = {
+ .suite_name = "event timer adapter test suite",
+ .setup = testsuite_setup,
+ .teardown = testsuite_teardown,
+ .unit_test_cases = {
+ TEST_CASE(adapter_create_free),
+ TEST_CASES_END() /**< NULL terminate unit test array */
+ }
+};
+
+static int
+test_event_timer_adapter_common(void)
+{
+ return unit_test_suite_runner(&adapter_tests);
+}
+
+REGISTER_TEST_COMMAND(event_timer_adapter_autotest,
+ test_event_timer_adapter_common);
--
2.6.4
Loading...