Hi Anatoly:
> -----Original Message-----
> From: Burakov, Anatoly
> Sent: Monday, June 18, 2018 6:36 PM
> To: Zhang, Qi Z <qi.z.zhang@intel.com>; thomas@monjalon.net
> Cc: Ananyev, Konstantin <konstantin.ananyev@intel.com>; dev@dpdk.org;
> Richardson, Bruce <bruce.richardson@intel.com>; Yigit, Ferruh
> <ferruh.yigit@intel.com>; Shelton, Benjamin H
> <benjamin.h.shelton@intel.com>; Vangati, Narender
> <narender.vangati@intel.com>
> Subject: Re: [PATCH 22/22] examples/devmgm_mp: add simple device
> management sample
>
> On 07-Jun-18 1:38 PM, Qi Zhang wrote:
> > The sample code demonstrate device (ethdev only) management at
> > multi-process envrionment. User can attach/detach a device on primary
> > process and see it is synced on secondary process automatically, also
> > user can lock a device to prevent it be detached or unlock it to go
> > back to default behaviour.
> >
> > How to start?
> > ./devmgm_mp --proc-type=auto
> >
> > Command Line Example:
> >
> >> help
> >> list
> >
> > /* attach a af_packet vdev */
> >> attach net_af_packet,iface=eth0
> >
> > /* detach port 0 */
> >> detach 0
> >
> > /* attach a private af_packet vdev (secondary process only)*/
> >> attachp net_af_packet,iface=eth0
> >
> > /* detach a private device (secondary process only) */
> >> detachp 0
> >
> > /* lock port 0 */
> >> lock 0
> >
> > /* unlock port 0 */
> >> unlock 0
> >
> > Signed-off-by: Qi Zhang <qi.z.zhang@intel.com>
> > ---
>
> I think the "devmgm_mp" is not a descriptive enough name. What this
> example demonstrates, is device hotplug. So how about naming the example
> app "hotplug"? (or "mp_hotplug" to indicate that it specifically sets out to
> demonstrate multiprocess hotplug)
Ok, I saw all the multi-process samples are in examples/multi_process, so I think this the right place to add
it could be "hotplug_mp" to follow other samples naming rule.
>
> > examples/devmgm_mp/Makefile | 64 +++++++
> > examples/devmgm_mp/commands.c | 383
> +++++++++++++++++++++++++++++++++++++++++
> > examples/devmgm_mp/commands.h | 10 ++
> > examples/devmgm_mp/main.c | 41 +++++
> > examples/devmgm_mp/meson.build | 11 ++
> > 5 files changed, 509 insertions(+)
> > create mode 100644 examples/devmgm_mp/Makefile
> > create mode 100644 examples/devmgm_mp/commands.c
> > create mode 100644 examples/devmgm_mp/commands.h
> > create mode 100644 examples/devmgm_mp/main.c
> > create mode 100644 examples/devmgm_mp/meson.build
> >
>
> <snip>
>
> > +#include <stdio.h>
> > +#include <stdint.h>
> > +#include <string.h>
> > +#include <stdlib.h>
> > +#include <stdarg.h>
> > +#include <errno.h>
> > +#include <netinet/in.h>
> > +#include <termios.h>
> > +#ifndef __linux__
> > + #ifdef __FreeBSD__
> > + #include <sys/socket.h>
> > + #else
> > + #include <net/socket.h>
> > + #endif
> > +#endif
>
> This seems like a weird define. Care to elaborate why are we checking for
> __linux__ not being defined?
OK, this is copy from exist sample code :), I will clean up the header file in v3.
>
> If you're trying to differentiate between Linux and FreeBSD, there's a readly
> RTE_EXEC_ENV_* config options, e.g.
>
> #ifdef RTE_EXEC_ENV_LINUXAPP
> // linux defines
> #endif
> #ifdef RTE_EXEC_ENV_BSDAPP
> // bsd defines
> #endif
>
> or something to that effect.
>
> > +
> > +#include <cmdline_rdline.h>
> > +#include <cmdline_parse.h>
> > +#include <cmdline_parse_ipaddr.h>
> > +#include <cmdline_parse_num.h>
> > +#include <cmdline_parse_string.h>
> > +#include <cmdline.h>
> > +#include <rte_ethdev.h>
>
> Generally (and as per DPDK coding guidelines), we prefer defines ordered as
> follows:
>
> 1) system defines enclosed in brackets
> 2) DPDK defines (rte_blah) enclosed in brackets
> 3) private/application-specific defines enclosed in quotes.
>
> All three groups should be separated by newline.
>
> So, these defines should've read as:
>
> #include <stdblah.h>
> #include <sys/blah.h>
>
> #include <rte_blah.h>
> #include <rte_foo.h>
>
> #include "cmdline_blah.h"
> #include "cmdline_foo.h"
Got it, thanks
>
> > +
> > +/**********************************************************/
> > +
> > +struct cmd_help_result {
> > + cmdline_fixed_string_t help;
> > +};
> > +
> > +static void cmd_help_parsed(__attribute__((unused)) void
> > +*parsed_result,
>
> <snip>
>
> > +{
> > + uint16_t port_id;
> > + char dev_name[RTE_DEV_NAME_MAX_LEN];
> > +
> > + cmdline_printf(cl, "list all etherdev\n");
> > +
> > + RTE_ETH_FOREACH_DEV(port_id) {
> > + rte_eth_dev_get_name_by_port(port_id, dev_name);
> > + /* Secondary process's ethdev->state may not be
> > + * updated after detach on primary process, but
> > + * ethdev->data should already be reset, so
> > + * use strlen(dev_name) == 0 to know the port is
> > + * not used.
> > + *
> > + * TODO: Secondary process should be informed when a
> > + * port is released on primary through mp channel.
> > + */
>
> That seems like a weird thing to leave out for TODO - it looks like an API
> deficiency. Can this be automatically updated on multiprocess hotplug sync, or
> somehow managed inside RTE_ETH_FOREACH_DEV?
>
> As i understand, per-process ethdev list is not protected by any locks, so doing
> this is racy. Since this is a multiprocess hotplug example app, it should
> demonstrate best practices. So, either RTE_ETH_FOREACH_DEV should be
> fixed to handle this case, or the application should demonstrate how to
> properly synchronize access to local device list. The latter is probably better as
> adding locking around ethdev device list is outside the scope of this patchset.
All this comment should be removed since TODO already done :)
Actually, we guarantee device be detached from secondary before primary.
>
> > + if (strlen(dev_name) > 0)
> > + cmdline_printf(cl, "%d\t%s\n", port_id, dev_name);
> > + else
> > + printf("empty dev_name is not expected!\n");
> > + }
>
> <snip>
>
> > +#include "commands.h"
> > +
> > +int main(int argc, char **argv)
> > +{
> > + int ret;
> > + struct cmdline *cl;
> > +
> > + ret = rte_eal_init(argc, argv);
> > + if (ret < 0)
> > + rte_panic("Cannot init EAL\n");
> > +
> > + cl = cmdline_stdin_new(main_ctx, "example> ");
> > + if (cl == NULL)
> > + rte_panic("Cannot create cmdline instance\n");
> > + cmdline_interact(cl);
> > + cmdline_stdin_exit(cl);
> > +
> > + return 0;
>
> Application should call rte_eal_cleanup() before exit. Otherwise, each
> secondary started and stopped will leak memory.
OK, will add it.
Thanks
Qi
>
> --
> Thanks,
> Anatoly
new file mode 100644
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2014 Intel Corporation
+
+# binary name
+APP = devmgm_mp
+
+# all source are stored in SRCS-y
+SRCS-y := main.c commands.c
+
+# Build using pkg-config variables if possible
+$(shell pkg-config --exists libdpdk)
+ifeq ($(.SHELLSTATUS),0)
+
+all: shared
+.PHONY: shared static
+shared: build/$(APP)-shared
+ ln -sf $(APP)-shared build/$(APP)
+static: build/$(APP)-static
+ ln -sf $(APP)-static build/$(APP)
+
+PC_FILE := $(shell pkg-config --path libdpdk)
+CFLAGS += -O3 $(shell pkg-config --cflags libdpdk)
+LDFLAGS_SHARED = $(shell pkg-config --libs libdpdk)
+LDFLAGS_STATIC = -Wl,-Bstatic $(shell pkg-config --static --libs libdpdk)
+
+build/$(APP)-shared: $(SRCS-y) Makefile $(PC_FILE) | build
+ $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED)
+
+build/$(APP)-static: $(SRCS-y) Makefile $(PC_FILE) | build
+ $(CC) $(CFLAGS) $(SRCS-y) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC)
+
+build:
+ @mkdir -p $@
+
+.PHONY: clean
+clean:
+ rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared
+ rmdir --ignore-fail-on-non-empty build
+
+else
+
+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 = devmgm_mp
+
+# all source are stored in SRCS-y
+SRCS-y := main.c commands.c
+
+CFLAGS += -O3
+CFLAGS += -DALLOW_EXPERIMENTAL_API
+CFLAGS += $(WERROR_FLAGS)
+CFLAGS_parse_obj_list.o := -D_GNU_SOURCE
+
+include $(RTE_SDK)/mk/rte.extapp.mk
+
+endif
new file mode 100644
@@ -0,0 +1,383 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2014 Intel Corporation.
+ * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <termios.h>
+#ifndef __linux__
+ #ifdef __FreeBSD__
+ #include <sys/socket.h>
+ #else
+ #include <net/socket.h>
+ #endif
+#endif
+
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_parse_ipaddr.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline.h>
+#include <rte_ethdev.h>
+
+/**********************************************************/
+
+struct cmd_help_result {
+ cmdline_fixed_string_t help;
+};
+
+static void cmd_help_parsed(__attribute__((unused)) void *parsed_result,
+ struct cmdline *cl,
+ __attribute__((unused)) void *data)
+{
+ cmdline_printf(cl,
+ "commands:\n"
+ "- attach <devargs>\n"
+ "- detach <port_id>\n"
+ "- attachp <devargs>\n"
+ "- detachp <port_id>\n"
+ "- lock <port_id>\n"
+ "- unlock <port_id>\n"
+ "- list\n\n");
+}
+
+cmdline_parse_token_string_t cmd_help_help =
+ TOKEN_STRING_INITIALIZER(struct cmd_help_result, help, "help");
+
+cmdline_parse_inst_t cmd_help = {
+ .f = cmd_help_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "show help",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_help_help,
+ NULL,
+ },
+};
+
+/**********************************************************/
+
+struct cmd_quit_result {
+ cmdline_fixed_string_t quit;
+};
+
+static void cmd_quit_parsed(__attribute__((unused)) void *parsed_result,
+ struct cmdline *cl,
+ __attribute__((unused)) void *data)
+{
+ cmdline_quit(cl);
+}
+
+cmdline_parse_token_string_t cmd_quit_quit =
+ TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit");
+
+cmdline_parse_inst_t cmd_quit = {
+ .f = cmd_quit_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "quit",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_quit_quit,
+ NULL,
+ },
+};
+
+/**********************************************************/
+
+struct cmd_list_result {
+ cmdline_fixed_string_t list;
+};
+
+static void cmd_list_parsed(__attribute__((unused)) void *parsed_result,
+ struct cmdline *cl,
+ __attribute__((unused)) void *data)
+{
+ uint16_t port_id;
+ char dev_name[RTE_DEV_NAME_MAX_LEN];
+
+ cmdline_printf(cl, "list all etherdev\n");
+
+ RTE_ETH_FOREACH_DEV(port_id) {
+ rte_eth_dev_get_name_by_port(port_id, dev_name);
+ /* Secondary process's ethdev->state may not be
+ * updated after detach on primary process, but
+ * ethdev->data should already be reset, so
+ * use strlen(dev_name) == 0 to know the port is
+ * not used.
+ *
+ * TODO: Secondary process should be informed when a
+ * port is released on primary through mp channel.
+ */
+ if (strlen(dev_name) > 0)
+ cmdline_printf(cl, "%d\t%s\n", port_id, dev_name);
+ else
+ printf("empty dev_name is not expected!\n");
+ }
+}
+
+cmdline_parse_token_string_t cmd_list_list =
+ TOKEN_STRING_INITIALIZER(struct cmd_list_result, list, "list");
+
+cmdline_parse_inst_t cmd_list = {
+ .f = cmd_list_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "list all devices",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_list_list,
+ NULL,
+ },
+};
+
+/**********************************************************/
+
+struct cmd_dev_attach_result {
+ cmdline_fixed_string_t attach;
+ cmdline_fixed_string_t device;
+};
+
+static void cmd_dev_attach_parsed(void *parsed_result,
+ struct cmdline *cl,
+ __attribute__((unused)) void *data)
+{
+ struct cmd_dev_attach_result *res = parsed_result;
+ uint16_t port_id;
+
+ if (!rte_eth_dev_attach(res->device, &port_id))
+ cmdline_printf(cl, "attached device %s at port %d\n",
+ res->device, port_id);
+ else
+ cmdline_printf(cl, "failed to attached device %s\n",
+ res->device);
+}
+
+cmdline_parse_token_string_t cmd_dev_attach_attach =
+ TOKEN_STRING_INITIALIZER(struct cmd_dev_attach_result, attach,
+ "attach");
+cmdline_parse_token_string_t cmd_dev_attach_device =
+ TOKEN_STRING_INITIALIZER(struct cmd_dev_attach_result, device, NULL);
+
+cmdline_parse_inst_t cmd_attach_device = {
+ .f = cmd_dev_attach_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "attach a device",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_dev_attach_attach,
+ (void *)&cmd_dev_attach_device,
+ NULL,
+ },
+};
+
+/**********************************************************/
+
+struct cmd_dev_attachp_result {
+ cmdline_fixed_string_t attachp;
+ cmdline_fixed_string_t device;
+};
+
+static void cmd_dev_attachp_parsed(void *parsed_result,
+ struct cmdline *cl,
+ __attribute__((unused)) void *data)
+{
+ struct cmd_dev_attachp_result *res = parsed_result;
+ uint16_t port_id;
+
+ if (!rte_eth_dev_attach_private(res->device, &port_id))
+ cmdline_printf(cl, "attached prviate device %s at port %d\n",
+ res->device, port_id);
+ else
+ cmdline_printf(cl, "failed to attached private device %s\n",
+ res->device);
+}
+
+cmdline_parse_token_string_t cmd_dev_attachp_attachp =
+ TOKEN_STRING_INITIALIZER(struct cmd_dev_attachp_result, attachp,
+ "attachp");
+cmdline_parse_token_string_t cmd_dev_attachp_device =
+ TOKEN_STRING_INITIALIZER(struct cmd_dev_attachp_result, device, NULL);
+
+cmdline_parse_inst_t cmd_attachp_device = {
+ .f = cmd_dev_attachp_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "attach a private device",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_dev_attachp_attachp,
+ (void *)&cmd_dev_attachp_device,
+ NULL,
+ },
+};
+
+/**********************************************************/
+
+struct cmd_dev_detach_result {
+ cmdline_fixed_string_t detach;
+ uint16_t port_id;
+};
+
+static void cmd_dev_detach_parsed(void *parsed_result,
+ struct cmdline *cl,
+ __attribute__((unused)) void *data)
+{
+ struct cmd_dev_detach_result *res = parsed_result;
+ uint16_t port_id = res->port_id;
+ char dev_name[RTE_DEV_NAME_MAX_LEN];
+
+ printf("detaching...\n");
+ if (!rte_eth_dev_detach(port_id, dev_name))
+ cmdline_printf(cl, "detached device at port %d\n",
+ port_id);
+ else
+ cmdline_printf(cl, "failed to dettached at port %d\n",
+ port_id);
+}
+
+cmdline_parse_token_string_t cmd_dev_detach_detach =
+ TOKEN_STRING_INITIALIZER(struct cmd_dev_detach_result, detach,
+ "detach");
+cmdline_parse_token_num_t cmd_dev_detach_port_id =
+ TOKEN_NUM_INITIALIZER(struct cmd_dev_detach_result, port_id, UINT16);
+
+cmdline_parse_inst_t cmd_detach_device = {
+ .f = cmd_dev_detach_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "detach a device",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_dev_detach_detach,
+ (void *)&cmd_dev_detach_port_id,
+ NULL,
+ },
+};
+
+/**********************************************************/
+
+struct cmd_dev_detachp_result {
+ cmdline_fixed_string_t detachp;
+ uint16_t port_id;
+};
+
+static void cmd_dev_detachp_parsed(void *parsed_result,
+ struct cmdline *cl,
+ __attribute__((unused)) void *data)
+{
+ struct cmd_dev_detachp_result *res = parsed_result;
+ uint16_t port_id = res->port_id;
+ char dev_name[RTE_DEV_NAME_MAX_LEN];
+
+ printf("detaching...\n");
+ if (!rte_eth_dev_detach_private(port_id, dev_name))
+ cmdline_printf(cl, "detached private device at port %d\n",
+ port_id);
+ else
+ cmdline_printf(cl, "failed to detach private device at port %d\n",
+ port_id);
+}
+
+cmdline_parse_token_string_t cmd_dev_detachp_detachp =
+ TOKEN_STRING_INITIALIZER(struct cmd_dev_detachp_result, detachp,
+ "detachp");
+cmdline_parse_token_num_t cmd_dev_detachp_port_id =
+ TOKEN_NUM_INITIALIZER(struct cmd_dev_detachp_result, port_id, UINT16);
+
+cmdline_parse_inst_t cmd_detachp_device = {
+ .f = cmd_dev_detachp_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "detach a private device",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_dev_detachp_detachp,
+ (void *)&cmd_dev_detachp_port_id,
+ NULL,
+ },
+};
+
+/**********************************************************/
+
+struct cmd_dev_lock_result {
+ cmdline_fixed_string_t lock;
+ uint16_t port_id;
+};
+
+static void cmd_dev_lock_parsed(void *parsed_result,
+ struct cmdline *cl,
+ __attribute__((unused)) void *data)
+{
+ struct cmd_dev_lock_result *res = parsed_result;
+ uint16_t port_id = res->port_id;
+ int ret = 0;
+
+ ret = rte_eth_dev_lock(res->port_id, NULL, NULL);
+ cmdline_printf(cl, "lock port %d, ret = %d\n", port_id, ret);
+}
+
+cmdline_parse_token_string_t cmd_dev_lock_lock =
+ TOKEN_STRING_INITIALIZER(struct cmd_dev_lock_result, lock, "lock");
+cmdline_parse_token_num_t cmd_dev_lock_port_id =
+ TOKEN_NUM_INITIALIZER(struct cmd_dev_lock_result, port_id, UINT16);
+
+cmdline_parse_inst_t cmd_lock_device = {
+ .f = cmd_dev_lock_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "lock a device",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_dev_lock_lock,
+ (void *)&cmd_dev_lock_port_id,
+ NULL,
+ },
+};
+
+/**********************************************************/
+
+struct cmd_dev_unlock_result {
+ cmdline_fixed_string_t unlock;
+ uint16_t port_id;
+};
+
+static void cmd_dev_unlock_parsed(void *parsed_result,
+ struct cmdline *cl,
+ __attribute__((unused)) void *data)
+{
+ struct cmd_dev_unlock_result *res = parsed_result;
+ uint16_t port_id = res->port_id;
+ int ret = 0;
+
+ ret = rte_eth_dev_unlock(res->port_id, NULL, NULL);
+ cmdline_printf(cl, "unlock port %d, ret = %d\n", port_id, ret);
+}
+
+cmdline_parse_token_string_t cmd_dev_unlock_unlock =
+ TOKEN_STRING_INITIALIZER(struct cmd_dev_unlock_result, unlock,
+ "unlock");
+cmdline_parse_token_num_t cmd_dev_unlock_port_id =
+ TOKEN_NUM_INITIALIZER(struct cmd_dev_unlock_result, port_id, UINT16);
+
+cmdline_parse_inst_t cmd_unlock_device = {
+ .f = cmd_dev_unlock_parsed, /* function to call */
+ .data = NULL, /* 2nd arg of func */
+ .help_str = "unlock a device",
+ .tokens = { /* token list, NULL terminated */
+ (void *)&cmd_dev_unlock_unlock,
+ (void *)&cmd_dev_unlock_port_id,
+ NULL,
+ },
+};
+
+/**********************************************************/
+/**********************************************************/
+/****** CONTEXT (list of instruction) */
+
+cmdline_parse_ctx_t main_ctx[] = {
+ (cmdline_parse_inst_t *)&cmd_help,
+ (cmdline_parse_inst_t *)&cmd_quit,
+ (cmdline_parse_inst_t *)&cmd_list,
+ (cmdline_parse_inst_t *)&cmd_attach_device,
+ (cmdline_parse_inst_t *)&cmd_detach_device,
+ (cmdline_parse_inst_t *)&cmd_attachp_device,
+ (cmdline_parse_inst_t *)&cmd_detachp_device,
+ (cmdline_parse_inst_t *)&cmd_lock_device,
+ (cmdline_parse_inst_t *)&cmd_unlock_device,
+ NULL,
+};
new file mode 100644
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2014 Intel Corporation
+ */
+
+#ifndef _COMMANDS_H_
+#define _COMMANDS_H_
+
+extern cmdline_parse_ctx_t main_ctx[];
+
+#endif /* _COMMANDS_H_ */
new file mode 100644
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2010-2014 Intel Corporation.
+ * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
+ * All rights reserved.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <termios.h>
+#include <sys/queue.h>
+
+#include <cmdline_rdline.h>
+#include <cmdline_parse.h>
+#include <cmdline_socket.h>
+#include <cmdline.h>
+
+#include <rte_memory.h>
+#include <rte_eal.h>
+#include <rte_debug.h>
+
+#include "commands.h"
+
+int main(int argc, char **argv)
+{
+ int ret;
+ struct cmdline *cl;
+
+ ret = rte_eal_init(argc, argv);
+ if (ret < 0)
+ rte_panic("Cannot init EAL\n");
+
+ cl = cmdline_stdin_new(main_ctx, "example> ");
+ if (cl == NULL)
+ rte_panic("Cannot create cmdline instance\n");
+ cmdline_interact(cl);
+ cmdline_stdin_exit(cl);
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2017 Intel Corporation
+
+# meson file, for building this example as part of a main DPDK build.
+#
+# To build this example as a standalone application with an already-installed
+# DPDK instance, use 'make'
+
+sources = files(
+ 'commands.c', 'main.c'
+)