[dpdk-dev,22/22] examples/devmgm_mp: add simple device management sample

Message ID 20180607123849.14439-23-qi.z.zhang@intel.com (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers
Series enable hotplug on multi-process |

Checks

Context Check Description
ci/Intel-compilation fail Compilation issues
ci/checkpatch success coding style OK

Commit Message

Qi Zhang June 7, 2018, 12:38 p.m. UTC
  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>
---
 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
  

Comments

Burakov, Anatoly June 18, 2018, 10:36 a.m. UTC | #1
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)

>   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?

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"

> +
> +/**********************************************************/
> +
> +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.

> +		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.
  
Qi Zhang June 22, 2018, 6:49 a.m. UTC | #2
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
  

Patch

diff --git a/examples/devmgm_mp/Makefile b/examples/devmgm_mp/Makefile
new file mode 100644
index 000000000..e6c0cb0c5
--- /dev/null
+++ b/examples/devmgm_mp/Makefile
@@ -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
diff --git a/examples/devmgm_mp/commands.c b/examples/devmgm_mp/commands.c
new file mode 100644
index 000000000..145cb766e
--- /dev/null
+++ b/examples/devmgm_mp/commands.c
@@ -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,
+};
diff --git a/examples/devmgm_mp/commands.h b/examples/devmgm_mp/commands.h
new file mode 100644
index 000000000..791204547
--- /dev/null
+++ b/examples/devmgm_mp/commands.h
@@ -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_ */
diff --git a/examples/devmgm_mp/main.c b/examples/devmgm_mp/main.c
new file mode 100644
index 000000000..f2f2e5a2f
--- /dev/null
+++ b/examples/devmgm_mp/main.c
@@ -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;
+}
diff --git a/examples/devmgm_mp/meson.build b/examples/devmgm_mp/meson.build
new file mode 100644
index 000000000..f916eb9af
--- /dev/null
+++ b/examples/devmgm_mp/meson.build
@@ -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'
+)