diff --git a/app/test/test_service_cores.c b/app/test/test_service_cores.c
index 46ed9a1868..f4abbd2dfe 100644
--- a/app/test/test_service_cores.c
+++ b/app/test/test_service_cores.c
@@ -1011,6 +1011,59 @@ service_may_be_active(void)
return unregister_all();
}
+static uint64_t busy_loops;
+static uint64_t idle_loops;
+
+static void maint_callback(bool work_done)
+{
+ if (work_done)
+ busy_loops++;
+ else
+ idle_loops++;
+}
+
+/* test registering and unregistering of service maint callback*/
+static int
+service_maintenance(void)
+{
+ const uint32_t sid = 0;
+ busy_loops = 0;
+ idle_loops = 0;
+ /* expected failure cases */
+ TEST_ASSERT_EQUAL(-EINVAL, rte_service_may_be_active(10000),
+ "Invalid service may be active check did not fail");
+
+ /* start the service */
+ TEST_ASSERT_EQUAL(0, rte_service_runstate_set(sid, 1),
+ "Starting valid service failed");
+ TEST_ASSERT_EQUAL(0, rte_service_lcore_add(slcore_id),
+ "Add service core failed when not in use before");
+ TEST_ASSERT_EQUAL(0, rte_service_map_lcore_set(sid, slcore_id, 1),
+ "Enabling valid service on valid core failed");
+ TEST_ASSERT_EQUAL(0,
rte_service_maint_callback_register(maint_callback, slcore_id),
+ "Registering maintenance callback failed");
+ TEST_ASSERT_EQUAL(0, rte_service_lcore_start(slcore_id),
+ "Service core start after add failed");
+
+ /* ensures core really is running the service function */
+ TEST_ASSERT_EQUAL(1, service_lcore_running_check(),
+ "Service core expected to poll service but it didn't");
+
+ /* ensures service maintenance callback received information about work
done */
+ TEST_ASSERT(busy_loops > 0, "No busy loops detected");
+ TEST_ASSERT(idle_loops > 0, "No idle loops detected");
+
+ /* stop the service, and wait for not-active with timeout */
+ TEST_ASSERT_EQUAL(0, rte_service_runstate_set(sid, 0),
+ "Error: Service stop returned non-zero");
+ TEST_ASSERT_EQUAL(0, service_ensure_stopped_with_timeout(sid),
+ "Error: Service not stopped after timeout period.");
+ TEST_ASSERT_EQUAL(0, rte_service_maint_callback_unregister(slcore_id),
+ "Unregistering service maintenance callback failed");
+
+ return unregister_all();
+}
+
/* check service may be active when service is running on a second lcore */
static int
service_active_two_cores(void)
@@ -1071,6 +1124,7 @@ static struct unit_test_suite service_tests = {
TEST_CASE_ST(dummy_register, NULL, service_mt_unsafe_poll),
TEST_CASE_ST(dummy_register, NULL, service_mt_safe_poll),
TEST_CASE_ST(dummy_register, NULL, service_may_be_active),
+ TEST_CASE_ST(dummy_register, NULL, service_maintenance),
TEST_CASE_ST(dummy_register, NULL, service_active_two_cores),
TEST_CASES_END() /**< NULL terminate unit test array */
}
diff --git a/lib/eal/common/rte_service.c b/lib/eal/common/rte_service.c
index dad3150df9..9c1fce8808 100644
--- a/lib/eal/common/rte_service.c
+++ b/lib/eal/common/rte_service.c
@@ -74,6 +74,7 @@ struct __rte_cache_aligned core_state {
RTE_BITSET_DECLARE(service_active_on_lcore, RTE_SERVICE_NUM_MAX);
RTE_ATOMIC(uint64_t) loops;
RTE_ATOMIC(uint64_t) cycles;
+ rte_maint_func maint_callback;
struct service_stats service_stats[RTE_SERVICE_NUM_MAX];
};
@@ -317,6 +318,30 @@ rte_service_component_runstate_set(uint32_t id, uint32_t runstate)
return 0;
}
+int32_t
+rte_service_maint_callback_register(rte_maint_func callback, uint32_t lcore)
+{
+ struct core_state *cs = RTE_LCORE_VAR_LCORE(lcore, lcore_states);
+ if (lcore >= RTE_MAX_LCORE || !cs->is_service_core
+ || callback == NULL)
+ return -EINVAL;
+
+ cs->maint_callback = callback;
+ return 0;
+}
+
+int32_t
+rte_service_maint_callback_unregister(uint32_t lcore)
+{
+ struct core_state *cs = RTE_LCORE_VAR_LCORE(lcore, lcore_states);
+ if (lcore >= RTE_MAX_LCORE || !cs->is_service_core
+ || !cs->maint_callback)
+ return -EINVAL;
+
+ cs->maint_callback = NULL;
+ return 0;
+}
+
int32_t
rte_service_runstate_set(uint32_t id, uint32_t runstate)
{
@@ -378,16 +403,17 @@ service_counter_add(RTE_ATOMIC(uint64_t) *counter,
uint64_t operand)
rte_memory_order_relaxed);
}
-static inline void
+static inline int32_t
service_runner_do_callback(struct rte_service_spec_impl *s,
struct core_state *cs, uint32_t service_idx)
{
rte_eal_trace_service_run_begin(service_idx, rte_lcore_id());
void *userdata = s->spec.callback_userdata;
+ int32_t rc;
if (service_stats_enabled(s)) {
uint64_t start = rte_rdtsc();
- int rc = s->spec.callback(userdata);
+ rc = s->spec.callback(userdata);
struct service_stats *service_stats =
&cs->service_stats[service_idx];
@@ -407,9 +433,10 @@ service_runner_do_callback(struct rte_service_spec_impl *s,
service_counter_add(&service_stats->cycles, cycles);
}
} else {
- s->spec.callback(userdata);
+ rc = s->spec.callback(userdata);
}
rte_eal_trace_service_run_end(service_idx, rte_lcore_id());
+ return rc;
}
@@ -436,16 +463,17 @@ service_run(uint32_t i, struct core_state *cs, const uint64_t *mapped_services,
rte_bitset_set(cs->service_active_on_lcore, i);
+ int32_t ret;
if ((service_mt_safe(s) == 0) && (serialize_mt_unsafe == 1)) {
if (!rte_spinlock_trylock(&s->execute_lock))
return -EBUSY;
- service_runner_do_callback(s, cs, i);
+ ret = service_runner_do_callback(s, cs, i);
rte_spinlock_unlock(&s->execute_lock);
} else
- service_runner_do_callback(s, cs, i);
+ ret = service_runner_do_callback(s, cs, i);
- return 0;
+ return ret;
}
int32_t
@@ -488,6 +516,10 @@ rte_service_run_iter_on_app_lcore(uint32_t id, uint32_t
serialize_mt_unsafe)
rte_atomic_fetch_sub_explicit(&s->num_mapped_cores, 1, rte_memory_order_relaxed);
+ /* Ignore service specific error codes */
+ if ((ret != -EINVAL) && (ret != -EBUSY) && (ret != -ENOEXEC))
+ ret = 0;
+
return ret;
}
@@ -505,13 +537,19 @@ service_runner_func(void *arg)
*/
while (rte_atomic_load_explicit(&cs->runstate,
rte_memory_order_acquire) ==
RUNSTATE_RUNNING) {
+ bool work_done = false;
ssize_t id;
RTE_BITSET_FOREACH_SET(id, cs->mapped_services, RTE_SERVICE_NUM_MAX) {
/* return value ignored as no change to code flow */
- service_run(id, cs, cs->mapped_services,
service_get(id), 1);
+ int32_t ret = service_run(id, cs, cs->mapped_services,
service_get(id), 1);
+ if ((ret != -EAGAIN) && (ret != -EINVAL) && (ret !=
-ENOEXEC))
+ work_done = true;
}
+ if (cs->maint_callback != NULL)
+ cs->maint_callback(work_done);
+
rte_atomic_store_explicit(&cs->loops, cs->loops + 1,
rte_memory_order_relaxed);
}
diff --git a/lib/eal/include/rte_service.h b/lib/eal/include/rte_service.h
index 1236fe2dc6..f8adff8dfe 100644
--- a/lib/eal/include/rte_service.h
+++ b/lib/eal/include/rte_service.h
@@ -336,6 +336,40 @@ int32_t rte_service_lcore_reset_all(void);
*/
int32_t rte_service_set_stats_enable(uint32_t id, int32_t enable);
+/**
+ * Signature of callback function to run after all services are done
+ *
+ * If registered, service framework will execute the callback
+ * indicating if any of the services run on the core was performing work.
+ * This may be used e.g. for maintenance or power saving when no work done.
+ */
+typedef void (*rte_maint_func)(bool work_done);
+
+/**
+ * Register service maintenance callback.
+ * @b EXPERIMENTAL: this API may change without prior notice.
+ *