[dpdk-dev] [PATCH] example: add new service cores sample application

Harry van Haaren harry.van.haaren at intel.com
Mon Sep 25 15:23:54 CEST 2017


This commit adds a new sample app, which showcases the value
of running services. In particular it allows the application
to dynamically schedule services to service-cores.

The sample app itself registers a number of dummy services,
and applies different profiles to them at runtime. Note that
this sample application does not forward any traffic - it
demonstrates advanced usage of the service cores API.

Signed-off-by: Harry van Haaren <harry.van.haaren at intel.com>
---
 examples/service_cores/Makefile |  54 +++++++++
 examples/service_cores/main.c   | 245 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 299 insertions(+)
 create mode 100644 examples/service_cores/Makefile
 create mode 100644 examples/service_cores/main.c

diff --git a/examples/service_cores/Makefile b/examples/service_cores/Makefile
new file mode 100644
index 0000000..bd4a345
--- /dev/null
+++ b/examples/service_cores/Makefile
@@ -0,0 +1,54 @@
+#   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.
+
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overridden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-gcc
+
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = service_cores
+
+# all source are stored in SRCS-y
+SRCS-y := main.c
+
+CFLAGS += $(WERROR_FLAGS)
+
+# workaround for a gcc bug with noreturn attribute
+# http://gcc.gnu.org/bugzilla/show_bug.cgi?id=12603
+ifeq ($(CONFIG_RTE_TOOLCHAIN_GCC),y)
+CFLAGS_main.o += -Wno-return-type
+endif
+
+include $(RTE_SDK)/mk/rte.extapp.mk
diff --git a/examples/service_cores/main.c b/examples/service_cores/main.c
new file mode 100644
index 0000000..c1f125a
--- /dev/null
+++ b/examples/service_cores/main.c
@@ -0,0 +1,245 @@
+/*
+ *   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 <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <sys/queue.h>
+
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_launch.h>
+#include <rte_eal.h>
+#include <rte_debug.h>
+#include <rte_cycles.h>
+
+/* allow application scheduling of the services */
+#include <rte_service.h>
+
+/* Allow application registration of its own services. An application does not
+ * have to register services, but it can be useful if it wishes to run a
+ * function on a core that is otherwise in use as a service core. In this
+ * example, all services are dummy services registered by the sample app itself.
+ */
+#include <rte_service_component.h>
+
+#define PROFILE_CORES_MAX 8
+#define PROFILE_SERVICE_PER_CORE 8
+
+/* dummy function to do "work" */
+static int32_t service_func(void *args)
+{
+	RTE_SET_USED(args);
+	rte_delay_us(2000);
+	return 0;
+}
+
+static struct rte_service_spec services[] = {
+	{"service_1", service_func, NULL, 0, 0},
+	{"service_2", service_func, NULL, 0, 0},
+	{"service_3", service_func, NULL, 0, 0},
+	{"service_4", service_func, NULL, 0, 0},
+	{"service_5", service_func, NULL, 0, 0},
+};
+#define NUM_SERVICES RTE_DIM(services)
+
+/* this struct holds the mapping of a particular core to all services */
+struct profile_for_core {
+	uint32_t mapped_services[PROFILE_SERVICE_PER_CORE];
+};
+
+/* struct that can be applied as the service core mapping. Items in this
+ * struct will be passed to the ordinary rte_service_* APIs to configure the
+ * service cores at runtime, based on the requirements.
+ *
+ * These profiles can be considered a "configuration" for the service cores,
+ * where switching profile just changes the number of cores and the mappings
+ * for each of them. As a result, the core requirements and performance of the
+ * application scales.
+ */
+struct profile {
+	char name[64];
+	uint32_t num_cores;
+	struct profile_for_core cores[PROFILE_CORES_MAX];
+};
+
+static struct profile profiles[] = {
+	/* profile 0: high performance */
+	{
+		.name = "High Performance",
+		.num_cores = 5,
+		.cores[0] = {.mapped_services = {1, 0, 0, 0, 0, 0, 0, 0} },
+		.cores[1] = {.mapped_services = {0, 1, 0, 0, 0, 0, 0, 0} },
+		.cores[2] = {.mapped_services = {0, 0, 1, 0, 0, 0, 0, 0} },
+		.cores[3] = {.mapped_services = {0, 0, 0, 1, 0, 0, 0, 0} },
+		.cores[4] = {.mapped_services = {0, 0, 0, 0, 1, 0, 0, 0} },
+	},
+	/* profile 1: mid performance with single service priority */
+	{
+		.name = "Mid-High Performance",
+		.num_cores = 3,
+		.cores[0] = {.mapped_services = {1, 1, 0, 0, 0, 0, 0, 0} },
+		.cores[1] = {.mapped_services = {0, 0, 1, 1, 0, 0, 0, 0} },
+		.cores[2] = {.mapped_services = {0, 0, 0, 0, 1, 0, 0, 0} },
+		.cores[3] = {.mapped_services = {0, 0, 0, 0, 0, 0, 0, 0} },
+		.cores[4] = {.mapped_services = {0, 0, 0, 0, 0, 0, 0, 0} },
+	},
+	/* profile 2: mid performance with single service priority */
+	{
+		.name = "Mid-Low Performance",
+		.num_cores = 2,
+		.cores[0] = {.mapped_services = {1, 1, 1, 0, 0, 0, 0, 0} },
+		.cores[1] = {.mapped_services = {1, 1, 0, 1, 1, 0, 0, 0} },
+		.cores[2] = {.mapped_services = {0, 0, 0, 0, 0, 0, 0, 0} },
+		.cores[3] = {.mapped_services = {0, 0, 0, 0, 0, 0, 0, 0} },
+		.cores[4] = {.mapped_services = {0, 0, 0, 0, 0, 0, 0, 0} },
+	},
+	/* profile 3: scale down performance on single core */
+	{
+		.name = "Scale down performance",
+		.num_cores = 1,
+		.cores[0] = {.mapped_services = {1, 1, 1, 1, 1, 0, 0, 0} },
+		.cores[1] = {.mapped_services = {0, 0, 0, 0, 0, 0, 0, 0} },
+		.cores[2] = {.mapped_services = {0, 0, 0, 0, 0, 0, 0, 0} },
+		.cores[3] = {.mapped_services = {0, 0, 0, 0, 0, 0, 0, 0} },
+		.cores[4] = {.mapped_services = {0, 0, 0, 0, 0, 0, 0, 0} },
+	},
+};
+#define NUM_PROFILES RTE_DIM(profiles)
+
+static int
+apply_profile(int profile_id)
+{
+	uint32_t i;
+	uint32_t s;
+	int ret;
+	struct profile *p = &profiles[profile_id];
+	const uint8_t core_off = 1;
+
+	for (i = 0; i < p->num_cores; i++) {
+		ret = rte_service_lcore_add(i + core_off);
+		if (ret && ret != -EALREADY)
+			printf("core %d added ret %d\n", i + core_off, ret);
+
+		ret = rte_service_lcore_start(i + core_off);
+		if (ret && ret != -EALREADY)
+			printf("core %d start ret %d\n", i + core_off, ret);
+
+		for (s = 0; s < NUM_SERVICES; s++) {
+			if (rte_service_map_lcore_set(s, i + core_off,
+					p->cores[i].mapped_services[s]))
+				rte_panic("failed to map lcore to 1\n");
+		}
+	}
+
+	for ( ; i < PROFILE_CORES_MAX; i++) {
+		for (s = 0; s < NUM_SERVICES; s++) {
+			ret = rte_service_map_lcore_set(s, i + core_off, 0);
+			if (ret && ret != -EINVAL) {
+				printf("%s %d: map lcore set = %d\n", __func__,
+						__LINE__, ret);
+			}
+		}
+		ret = rte_service_lcore_stop(i + core_off);
+		if (ret && ret != -EALREADY) {
+			printf("%s %d: lcore stop = %d\n", __func__,
+					__LINE__, ret);
+		}
+		ret = rte_service_lcore_del(i + core_off);
+		if (ret && ret != -EINVAL) {
+			printf("%s %d: lcore del = %d\n", __func__,
+					__LINE__, ret);
+		}
+	}
+}
+
+int
+main(int argc, char **argv)
+{
+	int ret;
+
+	ret = rte_eal_init(argc, argv);
+	if (ret < 0)
+		rte_panic("Cannot init EAL\n");
+
+	uint32_t i;
+	for (i = 0; i < NUM_SERVICES; i++) {
+		services[i].callback_userdata = 0;
+		uint32_t id;
+		ret = rte_service_component_register(&services[i], &id);
+		if (ret)
+			rte_exit(-1, "service register() failed");
+
+		/* set the service itself to be ready to run. In the case of
+		 * ethdev, eventdev etc PMDs, this will be set when the
+		 * appropriate configure or setup function is called.
+		 */
+		rte_service_component_runstate_set(id, 1);
+
+		/* Collect statistics for the service */
+		rte_service_set_stats_enable(id, 1);
+
+		/* the application sets the service to be active. Note that the
+		 * previous component_runstate_set() is the PMD indicating
+		 * ready, while this function is the application setting the
+		 * service to run. Applications can choose to not run a service
+		 * by setting runstate to 0 at any time.
+		 */
+		ret = rte_service_runstate_set(id, 1);
+		if (ret)
+			return -ENOEXEC;
+	}
+
+	i = 0;
+	while (1) {
+		const char clr[] = { 27, '[', '2', 'J', '\0' };
+		const char topLeft[] = { 27, '[', '1', ';', '1', 'H', '\0' };
+		printf("%s%s", clr, topLeft);
+
+		apply_profile(i);
+		printf("\n==> Profile: %s\n\n", profiles[i].name);
+
+		sleep(1);
+		rte_service_dump(stdout, UINT32_MAX);
+
+		sleep(5);
+		rte_service_dump(stdout, UINT32_MAX);
+
+		i++;
+		if (i >= NUM_PROFILES)
+			i = 0;
+	}
+
+	return 0;
+}
-- 
2.7.4



More information about the dev mailing list