[v3] eventdev/timer: add API to get remaining ticks

Message ID 20221219211135.2399687-1-erik.g.carrillo@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Jerin Jacob
Headers
Series [v3] eventdev/timer: add API to get remaining ticks |

Checks

Context Check Description
ci/checkpatch warning coding style issues
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/iol-x86_64-compile-testing success Testing PASS
ci/iol-x86_64-unit-testing success Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/Intel-compilation success Compilation OK
ci/iol-mellanox-Performance success Performance Testing PASS
ci/iol-aarch64-unit-testing success Testing PASS
ci/iol-aarch64-compile-testing success Testing PASS
ci/iol-intel-Performance success Performance Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/intel-Testing success Testing PASS
ci/github-robot: build success github build: passed
ci/iol-testing success Testing PASS

Commit Message

Carrillo, Erik G Dec. 19, 2022, 9:11 p.m. UTC
  Introduce an event timer adapter API which allows users to determine how
many adapter ticks remain until an event timer fires.

Signed-off-by: Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
---
v3:
* Handle ENOTSUP case in unit test

v2:
* Rename API to rte_event_timer_get_remaining_ticks
* Assert that API out param is non-NULL instead of checking and returning
  error

 app/test/test_event_timer_adapter.c    | 75 ++++++++++++++++++++++++++
 lib/eventdev/event_timer_adapter_pmd.h |  7 +++
 lib/eventdev/rte_event_timer_adapter.c | 52 ++++++++++++++++++
 lib/eventdev/rte_event_timer_adapter.h | 27 ++++++++++
 lib/eventdev/version.map               |  3 ++
 5 files changed, 164 insertions(+)
  

Comments

Jerin Jacob Jan. 13, 2023, 2:08 p.m. UTC | #1
On Tue, Dec 20, 2022 at 2:41 AM Erik Gabriel Carrillo
<erik.g.carrillo@intel.com> wrote:
>
> Introduce an event timer adapter API which allows users to determine how
> many adapter ticks remain until an event timer fires.
>
> Signed-off-by: Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
>
>  /**
> diff --git a/lib/eventdev/rte_event_timer_adapter.c b/lib/eventdev/rte_event_timer_adapter.c
> index a0f14bf861..ff762bb5f1 100644
> --- a/lib/eventdev/rte_event_timer_adapter.c
> +++ b/lib/eventdev/rte_event_timer_adapter.c
> @@ -8,6 +8,7 @@
>  #include <inttypes.h>
>  #include <stdbool.h>
>  #include <stdlib.h>
> +#include <math.h>
>
>  #include <rte_memzone.h>
>  #include <rte_errno.h>
> @@ -458,6 +459,21 @@ rte_event_timer_adapter_stats_reset(struct rte_event_timer_adapter *adapter)
>         return adapter->ops->stats_reset(adapter);
>  }
>
> +int
> +rte_event_timer_get_remaining_ticks(
> +                       const struct rte_event_timer_adapter *adapter,
> +                       const struct rte_event_timer *evtim,
> +                       uint64_t *ticks_remaining)
> +{
> +       ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
> +       FUNC_PTR_OR_ERR_RET(adapter->ops->get_remaining_ticks, -ENOTSUP);
> +
> +       RTE_ASSERT(ticks_remaining != NULL);

No ASSERT in library implementation. Return -ENIVAL

> +
> +       return adapter->ops->get_remaining_ticks(adapter, evtim,
> +                                                ticks_remaining);
> +}
> +
>  /*
>   * Software event timer adapter buffer helper functions
>   */
> @@ -1072,6 +1088,41 @@ swtim_stats_reset(const struct rte_event_timer_adapter *adapter)
>         return 0;
>  }
>
> +static int
> +swtim_get_remaining_ticks(const struct rte_event_timer_adapter *adapter,
> +                         const struct rte_event_timer *evtim,
> +                         uint64_t *ticks_remaining)
> +{
> +       uint64_t nsecs_per_adapter_tick, opaque, cycles_remaining;
> +       enum rte_event_timer_state n_state;
> +       double nsecs_per_cycle;
> +       struct rte_timer *tim;
> +       uint64_t cur_cycles;
> +
> +       /* Check that timer is armed */
> +       n_state = __atomic_load_n(&evtim->state, __ATOMIC_ACQUIRE);
> +       if (n_state != RTE_EVENT_TIMER_ARMED)
> +               return -EINVAL;
> +
> +       opaque = evtim->impl_opaque[0];
> +       tim = (struct rte_timer *)(uintptr_t)opaque;
> +
> +       cur_cycles = rte_get_timer_cycles();
> +       if (cur_cycles > tim->expire) {
> +               *ticks_remaining = 0;
> +               return 0;
> +       }
> +
> +       cycles_remaining = tim->expire - cur_cycles;
> +       nsecs_per_cycle = (double)NSECPERSEC / rte_get_timer_hz();
> +       nsecs_per_adapter_tick = adapter->data->conf.timer_tick_ns;
> +
> +       *ticks_remaining = (uint64_t)ceil((cycles_remaining * nsecs_per_cycle) /
> +                                         nsecs_per_adapter_tick);

Can RTE_*CEIL* in eal/include/rte_common.h" API helps here? Also, it
will remove the need for math.h, and it will be more optimized.


> +
> +       return 0;
> +}
> +
>  static uint16_t
>  __swtim_arm_burst(const struct rte_event_timer_adapter *adapter,
>                 struct rte_event_timer **evtims,
> @@ -1286,6 +1337,7 @@ static const struct event_timer_adapter_ops swtim_ops = {
>         .arm_burst = swtim_arm_burst,
>         .arm_tmo_tick_burst = swtim_arm_tmo_tick_burst,
>         .cancel_burst = swtim_cancel_burst,
> +       .get_remaining_ticks = swtim_get_remaining_ticks,
>  };
>
>  static int
> diff --git a/lib/eventdev/rte_event_timer_adapter.h b/lib/eventdev/rte_event_timer_adapter.h
> index cd10db19e4..1e43c09612 100644
> --- a/lib/eventdev/rte_event_timer_adapter.h
> +++ b/lib/eventdev/rte_event_timer_adapter.h
> @@ -678,6 +678,33 @@ rte_event_timer_cancel_burst(const struct rte_event_timer_adapter *adapter,
>         return adapter->cancel_burst(adapter, evtims, nb_evtims);
>  }
>
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Get the number of ticks remaining until an event timer fires.

fire vs expiry ? If it makes sense then use expiry

> + *
> + * @param adapter
> + *   A pointer to an event timer adapter structure
> + * @param evtim
> + *   A pointer to an rte_event_timer structure
> + * @param[out] ticks_remaining
> + *   Pointer to variable into which to write the number of ticks remaining
> + *   until the event timer fires

Same above comment

> + *
> + * @return
> + *   - 0: Success
> + *   - -EINVAL Invalid timer adapter identifier or the event timer is not in
> + *   the armed state

OR ticks_remaining is NULL

> + *   - -ENOTSUP The timer adapter implementation does not support this API.
> + */
> +__rte_experimental
> +int
> +rte_event_timer_get_remaining_ticks(
> +                       const struct rte_event_timer_adapter *adapter,
> +                       const struct rte_event_timer *evtim,
> +                       uint64_t *ticks_remaining);
> +
>  #ifdef __cplusplus
>  }
>  #endif
> diff --git a/lib/eventdev/version.map b/lib/eventdev/version.map
> index dd63ec6f68..2b702b6b15 100644
> --- a/lib/eventdev/version.map
> +++ b/lib/eventdev/version.map
> @@ -118,6 +118,9 @@ EXPERIMENTAL {
>         rte_event_eth_tx_adapter_instance_get;
>         rte_event_eth_tx_adapter_queue_start;
>         rte_event_eth_tx_adapter_queue_stop;
> +
> +       # added in 23.03
> +       rte_event_timer_get_remaining_ticks;

Please change to rte_event_timer_remaining_ticks_get(). All the
eventdev APIs, tried to use verb/action as last

Rest looks good to me. Good to merge then next version


>  };
>
>  INTERNAL {
> --
> 2.23.0
>

On Tue, Dec 20, 2022 at 2:41 AM Erik Gabriel Carrillo
<erik.g.carrillo@intel.com> wrote:
>
> Introduce an event timer adapter API which allows users to determine how
> many adapter ticks remain until an event timer fires.
>
> Signed-off-by: Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
> ---
> v3:
> * Handle ENOTSUP case in unit test
>
> v2:
> * Rename API to rte_event_timer_get_remaining_ticks
> * Assert that API out param is non-NULL instead of checking and returning
>   error
>
>  app/test/test_event_timer_adapter.c    | 75 ++++++++++++++++++++++++++
>  lib/eventdev/event_timer_adapter_pmd.h |  7 +++
>  lib/eventdev/rte_event_timer_adapter.c | 52 ++++++++++++++++++
>  lib/eventdev/rte_event_timer_adapter.h | 27 ++++++++++
>  lib/eventdev/version.map               |  3 ++
>  5 files changed, 164 insertions(+)
>
> diff --git a/app/test/test_event_timer_adapter.c b/app/test/test_event_timer_adapter.c
> index 1a440dfd10..6241a70597 100644
> --- a/app/test/test_event_timer_adapter.c
> +++ b/app/test/test_event_timer_adapter.c
> @@ -1920,6 +1920,79 @@ adapter_create_max(void)
>         return TEST_SUCCESS;
>  }
>
> +static inline int
> +test_timer_ticks_remaining(void)
> +{
> +       uint64_t ticks_remaining = UINT64_MAX;
> +       struct rte_event_timer *ev_tim;
> +       struct rte_event ev;
> +       int ret, i;
> +       const struct rte_event_timer tim = {
> +               .ev.op = RTE_EVENT_OP_NEW,
> +               .ev.queue_id = 0,
> +               .ev.sched_type = RTE_SCHED_TYPE_ATOMIC,
> +               .ev.priority = RTE_EVENT_DEV_PRIORITY_NORMAL,
> +               .ev.event_type =  RTE_EVENT_TYPE_TIMER,
> +               .state = RTE_EVENT_TIMER_NOT_ARMED,
> +       };
> +
> +       rte_mempool_get(eventdev_test_mempool, (void **)&ev_tim);
> +       *ev_tim = tim;
> +       ev_tim->ev.event_ptr = ev_tim;
> +#define TEST_TICKS 5
> +       ev_tim->timeout_ticks = CALC_TICKS(TEST_TICKS);
> +
> +       ret = rte_event_timer_get_remaining_ticks(timdev, ev_tim,
> +                                                 &ticks_remaining);
> +       if (ret == -ENOTSUP) {
> +               rte_mempool_put(eventdev_test_mempool, (void *)ev_tim);
> +               printf("API not supported, skipping test\n");
> +               return TEST_SKIPPED;
> +       }
> +
> +       /* Test that unarmed timer returns error */
> +       TEST_ASSERT_FAIL(ret,
> +                        "Didn't fail to get ticks for unarmed event timer");
> +
> +       TEST_ASSERT_EQUAL(rte_event_timer_arm_burst(timdev, &ev_tim, 1), 1,
> +                         "Failed to arm timer with proper timeout.");
> +       TEST_ASSERT_EQUAL(ev_tim->state, RTE_EVENT_TIMER_ARMED,
> +                         "Improper timer state set expected %d returned %d",
> +                         RTE_EVENT_TIMER_ARMED, ev_tim->state);
> +
> +       for (i = 0; i < TEST_TICKS; i++) {
> +               ret = rte_event_timer_get_remaining_ticks(timdev, ev_tim,
> +                                                         &ticks_remaining);
> +               if (ret < 0)
> +                       return TEST_FAILED;
> +
> +               TEST_ASSERT_EQUAL((int)ticks_remaining, TEST_TICKS - i,
> +                                 "Expected %d ticks remaining, got %"PRIu64"",
> +                                 TEST_TICKS - i, ticks_remaining);
> +
> +               rte_delay_ms(100);
> +       }
> +
> +       rte_delay_ms(100);
> +
> +       TEST_ASSERT_EQUAL(rte_event_dequeue_burst(evdev, 0, &ev, 1, 0), 1,
> +                         "Armed timer failed to trigger.");
> +       TEST_ASSERT_EQUAL(ev_tim->state, RTE_EVENT_TIMER_NOT_ARMED,
> +                         "Improper timer state set expected %d returned %d",
> +                         RTE_EVENT_TIMER_NOT_ARMED, ev_tim->state);
> +
> +       /* Test that timer that fired returns error */
> +       TEST_ASSERT_FAIL(rte_event_timer_get_remaining_ticks(timdev, ev_tim,
> +                                                          &ticks_remaining),
> +                        "Didn't fail to get ticks for unarmed event timer");
> +
> +       rte_mempool_put(eventdev_test_mempool, (void *)ev_tim);
> +
> +#undef TEST_TICKS
> +       return TEST_SUCCESS;
> +}
> +
> +
>  static struct unit_test_suite event_timer_adptr_functional_testsuite  = {
>         .suite_name = "event timer functional test suite",
>         .setup = testsuite_setup,
> @@ -1982,6 +2055,8 @@ static struct unit_test_suite event_timer_adptr_functional_testsuite  = {
>                 TEST_CASE_ST(timdev_setup_msec, timdev_teardown,
>                                 adapter_tick_resolution),
>                 TEST_CASE(adapter_create_max),
> +               TEST_CASE_ST(timdev_setup_msec, timdev_teardown,
> +                               test_timer_ticks_remaining),
>                 TEST_CASES_END() /**< NULL terminate unit test array */
>         }
>  };
> diff --git a/lib/eventdev/event_timer_adapter_pmd.h b/lib/eventdev/event_timer_adapter_pmd.h
> index 189017b5c1..7ba9df463b 100644
> --- a/lib/eventdev/event_timer_adapter_pmd.h
> +++ b/lib/eventdev/event_timer_adapter_pmd.h
> @@ -52,6 +52,11 @@ typedef int (*rte_event_timer_adapter_stats_get_t)(
>  typedef int (*rte_event_timer_adapter_stats_reset_t)(
>                 const struct rte_event_timer_adapter *adapter);
>  /**< @internal Reset statistics for event timer adapter */
> +typedef int (*rte_event_timer_get_remaining_ticks_t)(
> +               const struct rte_event_timer_adapter *adapter,
> +               const struct rte_event_timer *evtim,
> +               uint64_t *ticks_remaining);
> +/**< @internal Get remaining ticks for event timer */
>
>  /**
>   * @internal Structure containing the functions exported by an event timer
> @@ -74,6 +79,8 @@ struct event_timer_adapter_ops {
>         /**< Arm event timers with same expiration time */
>         rte_event_timer_cancel_burst_t          cancel_burst;
>         /**< Cancel one or more event timers */
> +       rte_event_timer_get_remaining_ticks_t   get_remaining_ticks;
> +       /**< Get remaining ticks for event timer */
>  };
>
>  /**
> diff --git a/lib/eventdev/rte_event_timer_adapter.c b/lib/eventdev/rte_event_timer_adapter.c
> index a0f14bf861..ff762bb5f1 100644
> --- a/lib/eventdev/rte_event_timer_adapter.c
> +++ b/lib/eventdev/rte_event_timer_adapter.c
> @@ -8,6 +8,7 @@
>  #include <inttypes.h>
>  #include <stdbool.h>
>  #include <stdlib.h>
> +#include <math.h>
>
>  #include <rte_memzone.h>
>  #include <rte_errno.h>
> @@ -458,6 +459,21 @@ rte_event_timer_adapter_stats_reset(struct rte_event_timer_adapter *adapter)
>         return adapter->ops->stats_reset(adapter);
>  }
>
> +int
> +rte_event_timer_get_remaining_ticks(
> +                       const struct rte_event_timer_adapter *adapter,
> +                       const struct rte_event_timer *evtim,
> +                       uint64_t *ticks_remaining)
> +{
> +       ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
> +       FUNC_PTR_OR_ERR_RET(adapter->ops->get_remaining_ticks, -ENOTSUP);
> +
> +       RTE_ASSERT(ticks_remaining != NULL);
> +
> +       return adapter->ops->get_remaining_ticks(adapter, evtim,
> +                                                ticks_remaining);
> +}
> +
>  /*
>   * Software event timer adapter buffer helper functions
>   */
> @@ -1072,6 +1088,41 @@ swtim_stats_reset(const struct rte_event_timer_adapter *adapter)
>         return 0;
>  }
>
> +static int
> +swtim_get_remaining_ticks(const struct rte_event_timer_adapter *adapter,
> +                         const struct rte_event_timer *evtim,
> +                         uint64_t *ticks_remaining)
> +{
> +       uint64_t nsecs_per_adapter_tick, opaque, cycles_remaining;
> +       enum rte_event_timer_state n_state;
> +       double nsecs_per_cycle;
> +       struct rte_timer *tim;
> +       uint64_t cur_cycles;
> +
> +       /* Check that timer is armed */
> +       n_state = __atomic_load_n(&evtim->state, __ATOMIC_ACQUIRE);
> +       if (n_state != RTE_EVENT_TIMER_ARMED)
> +               return -EINVAL;
> +
> +       opaque = evtim->impl_opaque[0];
> +       tim = (struct rte_timer *)(uintptr_t)opaque;
> +
> +       cur_cycles = rte_get_timer_cycles();
> +       if (cur_cycles > tim->expire) {
> +               *ticks_remaining = 0;
> +               return 0;
> +       }
> +
> +       cycles_remaining = tim->expire - cur_cycles;
> +       nsecs_per_cycle = (double)NSECPERSEC / rte_get_timer_hz();
> +       nsecs_per_adapter_tick = adapter->data->conf.timer_tick_ns;
> +
> +       *ticks_remaining = (uint64_t)ceil((cycles_remaining * nsecs_per_cycle) /
> +                                         nsecs_per_adapter_tick);
> +
> +       return 0;
> +}
> +
>  static uint16_t
>  __swtim_arm_burst(const struct rte_event_timer_adapter *adapter,
>                 struct rte_event_timer **evtims,
> @@ -1286,6 +1337,7 @@ static const struct event_timer_adapter_ops swtim_ops = {
>         .arm_burst = swtim_arm_burst,
>         .arm_tmo_tick_burst = swtim_arm_tmo_tick_burst,
>         .cancel_burst = swtim_cancel_burst,
> +       .get_remaining_ticks = swtim_get_remaining_ticks,
>  };
>
>  static int
> diff --git a/lib/eventdev/rte_event_timer_adapter.h b/lib/eventdev/rte_event_timer_adapter.h
> index cd10db19e4..1e43c09612 100644
> --- a/lib/eventdev/rte_event_timer_adapter.h
> +++ b/lib/eventdev/rte_event_timer_adapter.h
> @@ -678,6 +678,33 @@ rte_event_timer_cancel_burst(const struct rte_event_timer_adapter *adapter,
>         return adapter->cancel_burst(adapter, evtims, nb_evtims);
>  }
>
> +/**
> + * @warning
> + * @b EXPERIMENTAL: this API may change without prior notice
> + *
> + * Get the number of ticks remaining until an event timer fires.
> + *
> + * @param adapter
> + *   A pointer to an event timer adapter structure
> + * @param evtim
> + *   A pointer to an rte_event_timer structure
> + * @param[out] ticks_remaining
> + *   Pointer to variable into which to write the number of ticks remaining
> + *   until the event timer fires
> + *
> + * @return
> + *   - 0: Success
> + *   - -EINVAL Invalid timer adapter identifier or the event timer is not in
> + *   the armed state
> + *   - -ENOTSUP The timer adapter implementation does not support this API.
> + */
> +__rte_experimental
> +int
> +rte_event_timer_get_remaining_ticks(
> +                       const struct rte_event_timer_adapter *adapter,
> +                       const struct rte_event_timer *evtim,
> +                       uint64_t *ticks_remaining);
> +
>  #ifdef __cplusplus
>  }
>  #endif
> diff --git a/lib/eventdev/version.map b/lib/eventdev/version.map
> index dd63ec6f68..2b702b6b15 100644
> --- a/lib/eventdev/version.map
> +++ b/lib/eventdev/version.map
> @@ -118,6 +118,9 @@ EXPERIMENTAL {
>         rte_event_eth_tx_adapter_instance_get;
>         rte_event_eth_tx_adapter_queue_start;
>         rte_event_eth_tx_adapter_queue_stop;
> +
> +       # added in 23.03
> +       rte_event_timer_get_remaining_ticks;
>  };
>
>  INTERNAL {
> --
> 2.23.0
>
  
Carrillo, Erik G Jan. 13, 2023, 4:35 p.m. UTC | #2
Hi Jerin,

Thanks for the review.  One response in-line:

<...snipped...>

> > +static int
> > +swtim_get_remaining_ticks(const struct rte_event_timer_adapter
> *adapter,
> > +                         const struct rte_event_timer *evtim,
> > +                         uint64_t *ticks_remaining) {
> > +       uint64_t nsecs_per_adapter_tick, opaque, cycles_remaining;
> > +       enum rte_event_timer_state n_state;
> > +       double nsecs_per_cycle;
> > +       struct rte_timer *tim;
> > +       uint64_t cur_cycles;
> > +
> > +       /* Check that timer is armed */
> > +       n_state = __atomic_load_n(&evtim->state, __ATOMIC_ACQUIRE);
> > +       if (n_state != RTE_EVENT_TIMER_ARMED)
> > +               return -EINVAL;
> > +
> > +       opaque = evtim->impl_opaque[0];
> > +       tim = (struct rte_timer *)(uintptr_t)opaque;
> > +
> > +       cur_cycles = rte_get_timer_cycles();
> > +       if (cur_cycles > tim->expire) {
> > +               *ticks_remaining = 0;
> > +               return 0;
> > +       }
> > +
> > +       cycles_remaining = tim->expire - cur_cycles;
> > +       nsecs_per_cycle = (double)NSECPERSEC / rte_get_timer_hz();
> > +       nsecs_per_adapter_tick = adapter->data->conf.timer_tick_ns;
> > +
> > +       *ticks_remaining = (uint64_t)ceil((cycles_remaining *
> nsecs_per_cycle) /
> > +                                         nsecs_per_adapter_tick);
> 
> Can RTE_*CEIL* in eal/include/rte_common.h" API helps here? Also, it will
> remove the need for math.h, and it will be more optimized.
>
It looks like the RTE_*CEIL* macros are related to alignment, so they don't seem to apply here.

I'll make the suggested changes for the remainder of the comments in the next version of the patch. 
 
Thanks,
Erik

<...snipped...>
  

Patch

diff --git a/app/test/test_event_timer_adapter.c b/app/test/test_event_timer_adapter.c
index 1a440dfd10..6241a70597 100644
--- a/app/test/test_event_timer_adapter.c
+++ b/app/test/test_event_timer_adapter.c
@@ -1920,6 +1920,79 @@  adapter_create_max(void)
 	return TEST_SUCCESS;
 }
 
+static inline int
+test_timer_ticks_remaining(void)
+{
+	uint64_t ticks_remaining = UINT64_MAX;
+	struct rte_event_timer *ev_tim;
+	struct rte_event ev;
+	int ret, i;
+	const struct rte_event_timer tim = {
+		.ev.op = RTE_EVENT_OP_NEW,
+		.ev.queue_id = 0,
+		.ev.sched_type = RTE_SCHED_TYPE_ATOMIC,
+		.ev.priority = RTE_EVENT_DEV_PRIORITY_NORMAL,
+		.ev.event_type =  RTE_EVENT_TYPE_TIMER,
+		.state = RTE_EVENT_TIMER_NOT_ARMED,
+	};
+
+	rte_mempool_get(eventdev_test_mempool, (void **)&ev_tim);
+	*ev_tim = tim;
+	ev_tim->ev.event_ptr = ev_tim;
+#define TEST_TICKS 5
+	ev_tim->timeout_ticks = CALC_TICKS(TEST_TICKS);
+
+	ret = rte_event_timer_get_remaining_ticks(timdev, ev_tim,
+						  &ticks_remaining);
+	if (ret == -ENOTSUP) {
+		rte_mempool_put(eventdev_test_mempool, (void *)ev_tim);
+		printf("API not supported, skipping test\n");
+		return TEST_SKIPPED;
+	}
+
+	/* Test that unarmed timer returns error */
+	TEST_ASSERT_FAIL(ret,
+			 "Didn't fail to get ticks for unarmed event timer");
+
+	TEST_ASSERT_EQUAL(rte_event_timer_arm_burst(timdev, &ev_tim, 1), 1,
+			  "Failed to arm timer with proper timeout.");
+	TEST_ASSERT_EQUAL(ev_tim->state, RTE_EVENT_TIMER_ARMED,
+			  "Improper timer state set expected %d returned %d",
+			  RTE_EVENT_TIMER_ARMED, ev_tim->state);
+
+	for (i = 0; i < TEST_TICKS; i++) {
+		ret = rte_event_timer_get_remaining_ticks(timdev, ev_tim,
+							  &ticks_remaining);
+		if (ret < 0)
+			return TEST_FAILED;
+
+		TEST_ASSERT_EQUAL((int)ticks_remaining, TEST_TICKS - i,
+				  "Expected %d ticks remaining, got %"PRIu64"",
+				  TEST_TICKS - i, ticks_remaining);
+
+		rte_delay_ms(100);
+	}
+
+	rte_delay_ms(100);
+
+	TEST_ASSERT_EQUAL(rte_event_dequeue_burst(evdev, 0, &ev, 1, 0), 1,
+			  "Armed timer failed to trigger.");
+	TEST_ASSERT_EQUAL(ev_tim->state, RTE_EVENT_TIMER_NOT_ARMED,
+			  "Improper timer state set expected %d returned %d",
+			  RTE_EVENT_TIMER_NOT_ARMED, ev_tim->state);
+
+	/* Test that timer that fired returns error */
+	TEST_ASSERT_FAIL(rte_event_timer_get_remaining_ticks(timdev, ev_tim,
+							   &ticks_remaining),
+			 "Didn't fail to get ticks for unarmed event timer");
+
+	rte_mempool_put(eventdev_test_mempool, (void *)ev_tim);
+
+#undef TEST_TICKS
+	return TEST_SUCCESS;
+}
+
+
 static struct unit_test_suite event_timer_adptr_functional_testsuite  = {
 	.suite_name = "event timer functional test suite",
 	.setup = testsuite_setup,
@@ -1982,6 +2055,8 @@  static struct unit_test_suite event_timer_adptr_functional_testsuite  = {
 		TEST_CASE_ST(timdev_setup_msec, timdev_teardown,
 				adapter_tick_resolution),
 		TEST_CASE(adapter_create_max),
+		TEST_CASE_ST(timdev_setup_msec, timdev_teardown,
+				test_timer_ticks_remaining),
 		TEST_CASES_END() /**< NULL terminate unit test array */
 	}
 };
diff --git a/lib/eventdev/event_timer_adapter_pmd.h b/lib/eventdev/event_timer_adapter_pmd.h
index 189017b5c1..7ba9df463b 100644
--- a/lib/eventdev/event_timer_adapter_pmd.h
+++ b/lib/eventdev/event_timer_adapter_pmd.h
@@ -52,6 +52,11 @@  typedef int (*rte_event_timer_adapter_stats_get_t)(
 typedef int (*rte_event_timer_adapter_stats_reset_t)(
 		const struct rte_event_timer_adapter *adapter);
 /**< @internal Reset statistics for event timer adapter */
+typedef int (*rte_event_timer_get_remaining_ticks_t)(
+		const struct rte_event_timer_adapter *adapter,
+		const struct rte_event_timer *evtim,
+		uint64_t *ticks_remaining);
+/**< @internal Get remaining ticks for event timer */
 
 /**
  * @internal Structure containing the functions exported by an event timer
@@ -74,6 +79,8 @@  struct event_timer_adapter_ops {
 	/**< Arm event timers with same expiration time */
 	rte_event_timer_cancel_burst_t		cancel_burst;
 	/**< Cancel one or more event timers */
+	rte_event_timer_get_remaining_ticks_t	get_remaining_ticks;
+	/**< Get remaining ticks for event timer */
 };
 
 /**
diff --git a/lib/eventdev/rte_event_timer_adapter.c b/lib/eventdev/rte_event_timer_adapter.c
index a0f14bf861..ff762bb5f1 100644
--- a/lib/eventdev/rte_event_timer_adapter.c
+++ b/lib/eventdev/rte_event_timer_adapter.c
@@ -8,6 +8,7 @@ 
 #include <inttypes.h>
 #include <stdbool.h>
 #include <stdlib.h>
+#include <math.h>
 
 #include <rte_memzone.h>
 #include <rte_errno.h>
@@ -458,6 +459,21 @@  rte_event_timer_adapter_stats_reset(struct rte_event_timer_adapter *adapter)
 	return adapter->ops->stats_reset(adapter);
 }
 
+int
+rte_event_timer_get_remaining_ticks(
+			const struct rte_event_timer_adapter *adapter,
+			const struct rte_event_timer *evtim,
+			uint64_t *ticks_remaining)
+{
+	ADAPTER_VALID_OR_ERR_RET(adapter, -EINVAL);
+	FUNC_PTR_OR_ERR_RET(adapter->ops->get_remaining_ticks, -ENOTSUP);
+
+	RTE_ASSERT(ticks_remaining != NULL);
+
+	return adapter->ops->get_remaining_ticks(adapter, evtim,
+						 ticks_remaining);
+}
+
 /*
  * Software event timer adapter buffer helper functions
  */
@@ -1072,6 +1088,41 @@  swtim_stats_reset(const struct rte_event_timer_adapter *adapter)
 	return 0;
 }
 
+static int
+swtim_get_remaining_ticks(const struct rte_event_timer_adapter *adapter,
+			  const struct rte_event_timer *evtim,
+			  uint64_t *ticks_remaining)
+{
+	uint64_t nsecs_per_adapter_tick, opaque, cycles_remaining;
+	enum rte_event_timer_state n_state;
+	double nsecs_per_cycle;
+	struct rte_timer *tim;
+	uint64_t cur_cycles;
+
+	/* Check that timer is armed */
+	n_state = __atomic_load_n(&evtim->state, __ATOMIC_ACQUIRE);
+	if (n_state != RTE_EVENT_TIMER_ARMED)
+		return -EINVAL;
+
+	opaque = evtim->impl_opaque[0];
+	tim = (struct rte_timer *)(uintptr_t)opaque;
+
+	cur_cycles = rte_get_timer_cycles();
+	if (cur_cycles > tim->expire) {
+		*ticks_remaining = 0;
+		return 0;
+	}
+
+	cycles_remaining = tim->expire - cur_cycles;
+	nsecs_per_cycle = (double)NSECPERSEC / rte_get_timer_hz();
+	nsecs_per_adapter_tick = adapter->data->conf.timer_tick_ns;
+
+	*ticks_remaining = (uint64_t)ceil((cycles_remaining * nsecs_per_cycle) /
+					  nsecs_per_adapter_tick);
+
+	return 0;
+}
+
 static uint16_t
 __swtim_arm_burst(const struct rte_event_timer_adapter *adapter,
 		struct rte_event_timer **evtims,
@@ -1286,6 +1337,7 @@  static const struct event_timer_adapter_ops swtim_ops = {
 	.arm_burst = swtim_arm_burst,
 	.arm_tmo_tick_burst = swtim_arm_tmo_tick_burst,
 	.cancel_burst = swtim_cancel_burst,
+	.get_remaining_ticks = swtim_get_remaining_ticks,
 };
 
 static int
diff --git a/lib/eventdev/rte_event_timer_adapter.h b/lib/eventdev/rte_event_timer_adapter.h
index cd10db19e4..1e43c09612 100644
--- a/lib/eventdev/rte_event_timer_adapter.h
+++ b/lib/eventdev/rte_event_timer_adapter.h
@@ -678,6 +678,33 @@  rte_event_timer_cancel_burst(const struct rte_event_timer_adapter *adapter,
 	return adapter->cancel_burst(adapter, evtims, nb_evtims);
 }
 
+/**
+ * @warning
+ * @b EXPERIMENTAL: this API may change without prior notice
+ *
+ * Get the number of ticks remaining until an event timer fires.
+ *
+ * @param adapter
+ *   A pointer to an event timer adapter structure
+ * @param evtim
+ *   A pointer to an rte_event_timer structure
+ * @param[out] ticks_remaining
+ *   Pointer to variable into which to write the number of ticks remaining
+ *   until the event timer fires
+ *
+ * @return
+ *   - 0: Success
+ *   - -EINVAL Invalid timer adapter identifier or the event timer is not in
+ *   the armed state
+ *   - -ENOTSUP The timer adapter implementation does not support this API.
+ */
+__rte_experimental
+int
+rte_event_timer_get_remaining_ticks(
+			const struct rte_event_timer_adapter *adapter,
+			const struct rte_event_timer *evtim,
+			uint64_t *ticks_remaining);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/lib/eventdev/version.map b/lib/eventdev/version.map
index dd63ec6f68..2b702b6b15 100644
--- a/lib/eventdev/version.map
+++ b/lib/eventdev/version.map
@@ -118,6 +118,9 @@  EXPERIMENTAL {
 	rte_event_eth_tx_adapter_instance_get;
 	rte_event_eth_tx_adapter_queue_start;
 	rte_event_eth_tx_adapter_queue_stop;
+
+	# added in 23.03
+	rte_event_timer_get_remaining_ticks;
 };
 
 INTERNAL {