All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] Add support for using a config file for DPDK
@ 2017-05-30  8:30 Jacek Piasecki
  2017-05-30  8:30 ` [PATCH 1/3] cfgfile: add new API functions Jacek Piasecki
                   ` (2 more replies)
  0 siblings, 3 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-05-30  8:30 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, michalx.k.jastrzebski

From: Michal Jastrzebski <michalx.k.jastrzebski@intel.com>

This patch introduce a mechanism for running dpdk application with parameters
provided by configuration file. New API for cfgfile library allows to create
a cfgfile at runtime, add new section and add entry in a section - opens up
the possibility to have applications dynamically build up a proper DPDK
configuration, rather than having to have a pre-existing one.

A new API for EAL takes a config file data type - either loaded from file,
or built up programmatically in the application - and extracts DPDK parameters
from it to be used when eal init is called. This allows apps to have an alternative
method to configure EAL, other than via command-line parameters.

Testpmd application is used to the demonstrate the new eal API.
If a --cfgfile-path <path> option is passed into command line non EAL section,
then the file is loaded and used by app. If a file called config.ini is present
in current working directory, and no --cfgfile-path option is passed in,
config.ini file will be loaded and used by app.

Currently *.ini file format is supported, but we would like to demonstrate in
v2 how alternative config file formats could be used. Other work planned for v2:
support for subsections – allowing to pass i.e. [DPDK.vdev0] and its parameters,
cleanup to EAL API and testpmd.

Jacek Piasecki (1):
  cfgfile: add new API functions

Kuba Kozak (2):
  eal: add functions parsing EAL arguments
  app/testpmd: changed example to parse options from cfg file

 app/test-pmd/config.ini                         |   16 ++
 app/test-pmd/parameters.c                       |  135 ++++++++++-
 app/test-pmd/testpmd.c                          |   51 +++-
 app/test-pmd/testpmd.h                          |    2 +-
 lib/Makefile                                    |    6 +-
 lib/librte_cfgfile/Makefile                     |    1 +
 lib/librte_cfgfile/rte_cfgfile.c                |  293 +++++++++++++----------
 lib/librte_cfgfile/rte_cfgfile.h                |   52 ++++
 lib/librte_cfgfile/rte_cfgfile_version.map      |    9 +
 lib/librte_eal/bsdapp/eal/Makefile              |    4 +
 lib/librte_eal/bsdapp/eal/eal.c                 |  128 +++++++++-
 lib/librte_eal/bsdapp/eal/rte_eal_version.map   |    5 +
 lib/librte_eal/common/include/rte_eal.h         |    6 +
 lib/librte_eal/linuxapp/eal/Makefile            |    3 +
 lib/librte_eal/linuxapp/eal/eal.c               |  108 ++++++++-
 lib/librte_eal/linuxapp/eal/rte_eal_version.map |    4 +
 mk/rte.app.mk                                   |    2 +-
 17 files changed, 684 insertions(+), 141 deletions(-)
 create mode 100644 app/test-pmd/config.ini

-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH 1/3] cfgfile: add new API functions
  2017-05-30  8:30 [PATCH 0/3] Add support for using a config file for DPDK Jacek Piasecki
@ 2017-05-30  8:30 ` Jacek Piasecki
  2017-05-31 14:20   ` Bruce Richardson
  2017-06-26 10:59   ` [PATCH v2 0/7] Add support for using a config file for DPDK Jacek Piasecki
  2017-05-30  8:30 ` [PATCH 2/3] eal: add functions parsing EAL arguments Jacek Piasecki
  2017-05-30  8:30 ` [PATCH 3/3] app/testpmd: changed example to parse options from cfg file Jacek Piasecki
  2 siblings, 2 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-05-30  8:30 UTC (permalink / raw)
  To: dev
  Cc: bruce.richardson, deepak.k.jain, michalx.k.jastrzebski, Jacek Piasecki

Extend existing cfgfile library with providing new API functions:
rte_cfgfile_create() - create new cfgfile object
rte_cfgfile_add_section() - add new section to existing cfgfile object
rte_cfgfile_add_entry() - add new entry to existing cfgfile object
in specified section
This modification allows to create a cfgfile on runtime and
opens up the possibility to have applications dynamically build up
a proper DPDK configuration, rather than having to have a pre-existing one.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 lib/librte_cfgfile/Makefile                |    1 +
 lib/librte_cfgfile/rte_cfgfile.c           |  293 ++++++++++++++++------------
 lib/librte_cfgfile/rte_cfgfile.h           |   50 +++++
 lib/librte_cfgfile/rte_cfgfile_version.map |    9 +
 4 files changed, 227 insertions(+), 126 deletions(-)

diff --git a/lib/librte_cfgfile/Makefile b/lib/librte_cfgfile/Makefile
index 755ef11..0bee43e 100644
--- a/lib/librte_cfgfile/Makefile
+++ b/lib/librte_cfgfile/Makefile
@@ -38,6 +38,7 @@ LIB = librte_cfgfile.a
 
 CFLAGS += -O3
 CFLAGS += $(WERROR_FLAGS)
+CFLAGS += -I$(SRCDIR)/../librte_eal/common/include
 
 EXPORT_MAP := rte_cfgfile_version.map
 
diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
index b54a523..457f8be 100644
--- a/lib/librte_cfgfile/rte_cfgfile.c
+++ b/lib/librte_cfgfile/rte_cfgfile.c
@@ -35,8 +35,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+#include <errno.h>
 #include <rte_common.h>
-#include <rte_string_fns.h>
 
 #include "rte_cfgfile.h"
 
@@ -144,10 +144,6 @@ struct rte_cfgfile *
 rte_cfgfile_load_with_params(const char *filename, int flags,
 			     const struct rte_cfgfile_parameters *params)
 {
-	int allocated_sections = CFG_ALLOC_SECTION_BATCH;
-	int allocated_entries = 0;
-	int curr_section = -1;
-	int curr_entry = -1;
 	char buffer[CFG_NAME_LEN + CFG_VALUE_LEN + 4] = {0};
 	int lineno = 0;
 	struct rte_cfgfile *cfg = NULL;
@@ -159,28 +155,7 @@ struct rte_cfgfile *
 	if (f == NULL)
 		return NULL;
 
-	cfg = malloc(sizeof(*cfg) + sizeof(cfg->sections[0]) *
-		allocated_sections);
-	if (cfg == NULL)
-		goto error2;
-
-	memset(cfg->sections, 0, sizeof(cfg->sections[0]) * allocated_sections);
-
-	if (flags & CFG_FLAG_GLOBAL_SECTION) {
-		curr_section = 0;
-		allocated_entries = CFG_ALLOC_ENTRY_BATCH;
-		cfg->sections[curr_section] = malloc(
-			sizeof(*cfg->sections[0]) +
-			sizeof(cfg->sections[0]->entries[0]) *
-			allocated_entries);
-		if (cfg->sections[curr_section] == NULL) {
-			printf("Error - no memory for global section\n");
-			goto error1;
-		}
-
-		snprintf(cfg->sections[curr_section]->name,
-				 sizeof(cfg->sections[0]->name), "GLOBAL");
-	}
+	cfg = rte_cfgfile_create(flags);
 
 	while (fgets(buffer, sizeof(buffer), f) != NULL) {
 		char *pos = NULL;
@@ -191,6 +166,7 @@ struct rte_cfgfile *
 					"Check if line too long\n", lineno);
 			goto error1;
 		}
+		/* skip parsing if comment character found */
 		pos = memchr(buffer, params->comment_character, len);
 		if (pos != NULL) {
 			*pos = '\0';
@@ -198,6 +174,7 @@ struct rte_cfgfile *
 		}
 
 		len = _strip(buffer, len);
+		/* if not section line go to parse entry line */
 		if (buffer[0] != '[' && memchr(buffer, '=', len) == NULL)
 			continue;
 
@@ -212,121 +189,185 @@ struct rte_cfgfile *
 			*end = '\0';
 			_strip(&buffer[1], end - &buffer[1]);
 
-			/* close off old section and add start new one */
-			if (curr_section >= 0)
-				cfg->sections[curr_section]->num_entries =
-					curr_entry + 1;
-			curr_section++;
-
-			/* resize overall struct if we don't have room for more
-			sections */
-			if (curr_section == allocated_sections) {
-				allocated_sections += CFG_ALLOC_SECTION_BATCH;
-				struct rte_cfgfile *n_cfg = realloc(cfg,
-					sizeof(*cfg) + sizeof(cfg->sections[0])
-					* allocated_sections);
-				if (n_cfg == NULL) {
-					curr_section--;
-					printf("Error - no more memory\n");
-					goto error1;
-				}
-				cfg = n_cfg;
-			}
-
-			/* allocate space for new section */
-			allocated_entries = CFG_ALLOC_ENTRY_BATCH;
-			curr_entry = -1;
-			cfg->sections[curr_section] = malloc(
-				sizeof(*cfg->sections[0]) +
-				sizeof(cfg->sections[0]->entries[0]) *
-				allocated_entries);
-			if (cfg->sections[curr_section] == NULL) {
-				printf("Error - no more memory\n");
-				goto error1;
-			}
-
-			snprintf(cfg->sections[curr_section]->name,
-					sizeof(cfg->sections[0]->name),
-					"%s", &buffer[1]);
+			rte_cfgfile_add_section(&cfg, &buffer[1]);
 		} else {
 			/* value line */
-			if (curr_section < 0) {
-				printf("Error line %d - value outside of"
-					"section\n", lineno);
-				goto error1;
-			}
-
-			struct rte_cfgfile_section *sect =
-				cfg->sections[curr_section];
-			int n;
 			char *split[2] = {NULL};
-			n = rte_strsplit(buffer, sizeof(buffer), split, 2, '=');
-			if (flags & CFG_FLAG_EMPTY_VALUES) {
-				if ((n < 1) || (n > 2)) {
-					printf("Error at line %d - cannot split string, n=%d\n",
-					       lineno, n);
-					goto error1;
-				}
-			} else {
-				if (n != 2) {
-					printf("Error at line %d - cannot split string, n=%d\n",
-					       lineno, n);
-					goto error1;
-				}
-			}
 
-			curr_entry++;
-			if (curr_entry == allocated_entries) {
-				allocated_entries += CFG_ALLOC_ENTRY_BATCH;
-				struct rte_cfgfile_section *n_sect = realloc(
-					sect, sizeof(*sect) +
-					sizeof(sect->entries[0]) *
-					allocated_entries);
-				if (n_sect == NULL) {
-					curr_entry--;
-					printf("Error - no more memory\n");
-					goto error1;
-				}
-				sect = cfg->sections[curr_section] = n_sect;
-			}
-
-			sect->entries[curr_entry] = malloc(
-				sizeof(*sect->entries[0]));
-			if (sect->entries[curr_entry] == NULL) {
-				printf("Error - no more memory\n");
+			split[0] = buffer;
+			split[1] = memchr(buffer, '=', len);
+			if ((split[1] == NULL) &&
+					!(flags & CFG_FLAG_EMPTY_VALUES)) {
+				printf("Error at line %d - cannot split "
+					"string\n", lineno);
 				goto error1;
 			}
-
-			struct rte_cfgfile_entry *entry = sect->entries[
-				curr_entry];
-			snprintf(entry->name, sizeof(entry->name), "%s",
-				split[0]);
-			snprintf(entry->value, sizeof(entry->value), "%s",
-				 split[1] ? split[1] : "");
-			_strip(entry->name, strnlen(entry->name,
-				sizeof(entry->name)));
-			_strip(entry->value, strnlen(entry->value,
-				sizeof(entry->value)));
+			if (split[1] != NULL) {
+				*split[1] = '\0';
+				split[1]++;
+			}
+			if (cfg->num_sections == 0)
+				goto error1;
+			rte_cfgfile_add_entry(cfg,
+				cfg->sections[cfg->num_sections - 1]->name,
+					split[0], split[1] ? split[1] : "");
 		}
 	}
 	fclose(f);
-	cfg->flags = flags;
-	cfg->num_sections = curr_section + 1;
-	/* curr_section will still be -1 if we have an empty file */
-	if (curr_section >= 0)
-		cfg->sections[curr_section]->num_entries = curr_entry + 1;
 	return cfg;
-
 error1:
-	cfg->num_sections = curr_section + 1;
-	if (curr_section >= 0)
-		cfg->sections[curr_section]->num_entries = curr_entry + 1;
 	rte_cfgfile_close(cfg);
-error2:
 	fclose(f);
 	return NULL;
 }
 
+struct rte_cfgfile *
+rte_cfgfile_create(int flags)
+{
+	int allocated_sections = CFG_ALLOC_SECTION_BATCH;
+	struct rte_cfgfile *cfg = NULL;
+
+	cfg = malloc(sizeof(*cfg) + sizeof(cfg->sections[0]) *
+			allocated_sections);
+	if (cfg == NULL) {
+		printf("Error - no memory to create cfgfile\n");
+		return NULL;
+	}
+
+	memset(cfg->sections, 0, sizeof(cfg->sections[0]) * allocated_sections);
+
+	cfg->flags = flags;
+	cfg->num_sections = 0;
+
+	if (flags & CFG_FLAG_GLOBAL_SECTION)
+		rte_cfgfile_add_section(&cfg, "GLOBAL");
+	return cfg;
+}
+
+int
+rte_cfgfile_add_section(struct rte_cfgfile **cfgfile, const char *sectionname)
+{
+	struct rte_cfgfile *cfg = *cfgfile;
+	int curr_section = cfg->num_sections - 1;
+	int allocated_sections = 0;
+
+	/* check if given section already exist */
+	if (rte_cfgfile_has_section(cfg, sectionname) != 0) {
+		printf("Given section '%s' already exist\n", sectionname);
+		return 0;
+	}
+	/* calculate allocated sections from number of sections */
+	if ((cfg->num_sections) != 0)
+		allocated_sections = (cfg->num_sections/
+			CFG_ALLOC_SECTION_BATCH + 1) * CFG_ALLOC_SECTION_BATCH;
+
+	curr_section++;
+	/* resize overall struct if we don't have room for more	sections */
+	if (curr_section == allocated_sections) {
+		allocated_sections += CFG_ALLOC_SECTION_BATCH;
+		struct rte_cfgfile *n_cfg = realloc(cfg,
+			sizeof(*cfg) + sizeof(cfg->sections[0])
+			* allocated_sections);
+		if (n_cfg == NULL)
+			return -ENOMEM;
+		*cfgfile = n_cfg;
+		cfg = *cfgfile;
+	}
+	/* allocate space for new section */
+	cfg->sections[curr_section] = malloc(
+		sizeof(*cfg->sections[0]) +
+		sizeof(cfg->sections[0]->entries[0]) *
+		CFG_ALLOC_ENTRY_BATCH);
+
+	if (cfg->sections[curr_section] == NULL)
+		return -ENOMEM;
+
+	snprintf(cfg->sections[curr_section]->name,
+			sizeof(cfg->sections[0]->name), "%s", sectionname);
+
+	cfg->sections[curr_section]->num_entries = 0;
+	cfg->num_sections = curr_section + 1;
+	return 0;
+}
+
+static int
+_get_section_index(struct rte_cfgfile *cfgfile, const char *sectionname) {
+	int i;
+
+	for (i = 0; i < cfgfile->num_sections; i++) {
+		if (strncmp(cfgfile->sections[i]->name, sectionname,
+				sizeof(cfgfile->sections[0]->name)) == 0)
+			return i;
+	}
+	return -1;
+}
+
+static signed int
+_add_entry(struct rte_cfgfile *cfgfile, const signed int curr_section,
+		const char *entryname, const char *entryvalue)
+{
+	int allocated_entries = 0;
+	int curr_entry = cfgfile->sections[curr_section]->num_entries - 1;
+
+	/* calculate allocated entries from number of entries */
+	if ((curr_entry + 1) != 0)
+		allocated_entries = ((curr_entry + 1)/
+			CFG_ALLOC_ENTRY_BATCH + 1) * CFG_ALLOC_ENTRY_BATCH;
+
+	curr_entry++;
+	struct rte_cfgfile_section *sect = cfgfile->sections[curr_section];
+
+	sect->entries[curr_entry] = malloc(sizeof(*sect->entries[0]));
+
+	if (curr_entry == allocated_entries) {
+		allocated_entries += CFG_ALLOC_ENTRY_BATCH;
+		struct rte_cfgfile_section *n_sect = realloc(
+			sect, sizeof(*sect) +
+			sizeof(sect->entries[0]) *
+			allocated_entries);
+		if (n_sect == NULL)
+			return -ENOMEM;
+		sect = cfgfile->sections[curr_section] = n_sect;
+	}
+
+	if (sect->entries[curr_entry] == NULL) {
+		cfgfile->num_sections = curr_section + 1;
+		if (curr_section >= 0) {
+			cfgfile->sections[curr_section]->num_entries =
+								curr_entry + 1;
+			return -ENOMEM;
+		}
+	}
+
+	struct rte_cfgfile_entry *entry = sect->entries[curr_entry];
+
+	snprintf(entry->name, sizeof(entry->name), "%s", entryname);
+	snprintf(entry->value, sizeof(entry->value), "%s", entryvalue);
+	_strip(entry->name, strnlen(entry->name, sizeof(entry->name)));
+	_strip(entry->value, strnlen(entry->value, sizeof(entry->value)));
+
+	cfgfile->sections[curr_section]->num_entries = curr_entry + 1;
+
+	return 0;
+};
+
+int rte_cfgfile_add_entry(struct rte_cfgfile *cfgfile, const char *sectionname,
+		const char *entryname, const char *entryvalue)
+{
+	int curr_section;
+
+	/* check if given entry in specified section already exist */
+	if (rte_cfgfile_has_entry(cfgfile, sectionname, entryname) != 0)
+		return 0;
+
+	/* search for section index by sectionname */
+	curr_section = _get_section_index(cfgfile, sectionname);
+
+	if (curr_section < 0)
+		return -EINVAL;
+
+	return _add_entry(cfgfile, curr_section, entryname, entryvalue);
+}
 
 int rte_cfgfile_close(struct rte_cfgfile *cfg)
 {
diff --git a/lib/librte_cfgfile/rte_cfgfile.h b/lib/librte_cfgfile/rte_cfgfile.h
index fa10d40..902ec69 100644
--- a/lib/librte_cfgfile/rte_cfgfile.h
+++ b/lib/librte_cfgfile/rte_cfgfile.h
@@ -121,6 +121,56 @@ struct rte_cfgfile *rte_cfgfile_load_with_params(const char *filename,
 	int flags, const struct rte_cfgfile_parameters *params);
 
 /**
+ * Create new empty cfgfile instance.
+ *
+ * @param flags
+ *   CFG_FLAG_GLOBAL_SECTION
+ *   Indicates that the file supports key value entries before the first
+ *   defined section.  These entries can be accessed in the "GLOBAL"
+ *   section.
+ *   CFG_FLAG_EMPTY_VALUES
+ *   Indicates that file supports key value entries where the value can
+ *   be zero length (e.g., "key=").
+ * @return
+ *   Handle to empty cfgfile instance on success, NULL on error
+ */
+struct rte_cfgfile *rte_cfgfile_create(int flags);
+
+/**
+ * Add section in cfgfile instance.
+ *
+ * @param cfgfile
+ *   A pointer to a pointer to the cfgfile structure, this approach enables
+ *   cfgfile object passing to and after required change
+ *   returning from function.
+ * @param sectionname
+ *   Section name which will be add to cfgfile.
+ * @return
+ *   value 0 on success, value different than 0 otherwise
+ */
+int
+rte_cfgfile_add_section(struct rte_cfgfile **cfgfile, const char *sectionname);
+
+/**
+ * Add entry in cfgfile instance.
+ *
+ * @param cfgfile
+ *   A pointer to a pointer to the cfgfile structure, this approach enables
+ *   cfgfile object passing to and after required change
+ *   returning from function.
+ * @param sectionname
+ *   Given section name to add an entry.
+ * @param entryname
+ *   Entry name.
+ * @param entryvalue
+ *   Entry value.
+ * @return
+ *   value 0 on success, value different than 0 otherwise
+ */
+int rte_cfgfile_add_entry(struct rte_cfgfile *cfgfile, const char *sectionname,
+				const char *entryname, const char *entryvalue);
+
+/**
 * Get number of sections in config file
 *
 * @param cfg
diff --git a/lib/librte_cfgfile/rte_cfgfile_version.map b/lib/librte_cfgfile/rte_cfgfile_version.map
index 5fe60f7..6ed7a4e 100644
--- a/lib/librte_cfgfile/rte_cfgfile_version.map
+++ b/lib/librte_cfgfile/rte_cfgfile_version.map
@@ -27,3 +27,12 @@ DPDK_17.05 {
     rte_cfgfile_load_with_params;
 
 } DPDK_16.04;
+
+DPDK_17.08 {
+	global:
+
+	rte_cfgfile_add_entry;
+	rte_cfgfile_add_section;
+	rte_cfgfile_create;
+
+} DPDK_17.05;
\ No newline at end of file
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 2/3] eal: add functions parsing EAL arguments
  2017-05-30  8:30 [PATCH 0/3] Add support for using a config file for DPDK Jacek Piasecki
  2017-05-30  8:30 ` [PATCH 1/3] cfgfile: add new API functions Jacek Piasecki
@ 2017-05-30  8:30 ` Jacek Piasecki
  2017-05-31 15:46   ` Bruce Richardson
  2017-05-30  8:30 ` [PATCH 3/3] app/testpmd: changed example to parse options from cfg file Jacek Piasecki
  2 siblings, 1 reply; 70+ messages in thread
From: Jacek Piasecki @ 2017-05-30  8:30 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, michalx.k.jastrzebski, Kuba Kozak

From: Kuba Kozak <kubax.kozak@intel.com>

added function rte_eal_configure which translate
options from config file into argc, **argv form.

changed function rte_eal_init to meld
argc, argv** options from config file with
these from command line and then parse as
before

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 lib/Makefile                                    |    6 +-
 lib/librte_cfgfile/rte_cfgfile.h                |    2 +
 lib/librte_eal/bsdapp/eal/Makefile              |    4 +
 lib/librte_eal/bsdapp/eal/eal.c                 |  128 ++++++++++++++++++++++-
 lib/librte_eal/bsdapp/eal/rte_eal_version.map   |    5 +
 lib/librte_eal/common/include/rte_eal.h         |    6 ++
 lib/librte_eal/linuxapp/eal/Makefile            |    3 +
 lib/librte_eal/linuxapp/eal/eal.c               |  108 ++++++++++++++++++-
 lib/librte_eal/linuxapp/eal/rte_eal_version.map |    4 +
 mk/rte.app.mk                                   |    2 +-
 10 files changed, 261 insertions(+), 7 deletions(-)

diff --git a/lib/Makefile b/lib/Makefile
index 07e1fd0..fc5df3a 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -32,7 +32,11 @@
 include $(RTE_SDK)/mk/rte.vars.mk
 
 DIRS-y += librte_compat
+DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile
 DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+DEPDIRS-librte_eal := librte_cfgfile
+endif
 DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
 DEPDIRS-librte_ring := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
@@ -41,8 +45,6 @@ DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
 DEPDIRS-librte_mbuf := librte_eal librte_mempool
 DIRS-$(CONFIG_RTE_LIBRTE_TIMER) += librte_timer
 DEPDIRS-librte_timer := librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile
-DEPDIRS-librte_cfgfile := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_CMDLINE) += librte_cmdline
 DEPDIRS-librte_cmdline := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_ETHER) += librte_ether
diff --git a/lib/librte_cfgfile/rte_cfgfile.h b/lib/librte_cfgfile/rte_cfgfile.h
index 902ec69..8808aa7 100644
--- a/lib/librte_cfgfile/rte_cfgfile.h
+++ b/lib/librte_cfgfile/rte_cfgfile.h
@@ -49,6 +49,8 @@
 *
 ***/
 
+struct rte_cfgfile; /* forward declaration of struct */
+
 #ifndef CFG_NAME_LEN
 #define CFG_NAME_LEN 64
 #endif
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index a0f9950..d70eefb 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -50,6 +50,10 @@ EXPORT_MAP := rte_eal_version.map
 
 LIBABIVER := 4
 
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -lrte_cfgfile
+endif
+
 # specific to bsdapp exec-env
 SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) := eal.c
 SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += eal_memory.c
diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index 05f0c1f..e5e11c8 100644
--- a/lib/librte_eal/bsdapp/eal/eal.c
+++ b/lib/librte_eal/bsdapp/eal/eal.c
@@ -73,6 +73,7 @@
 #include <rte_version.h>
 #include <rte_atomic.h>
 #include <malloc_heap.h>
+#include <rte_cfgfile.h>
 
 #include "eal_private.h"
 #include "eal_thread.h"
@@ -92,6 +93,12 @@
  * duration of the program, as we hold a write lock on it in the primary proc */
 static int mem_cfg_fd = -1;
 
+static int cfg_argc;
+#ifdef RTE_LIBRTE_CFGFILE
+static char **cfg_argv;
+#endif
+
+
 static struct flock wr_lock = {
 		.l_type = F_WRLCK,
 		.l_whence = SEEK_SET,
@@ -492,10 +499,28 @@ static void rte_eal_init_alert(const char *msg)
 	RTE_LOG(ERR, EAL, "%s\n", msg);
 }
 
+#ifdef RTE_LIBRTE_CFGFILE
+static void
+free_pointer_array(char **pkt)
+{
+	int i = 0;
+
+	if (pkt) {
+		while (pkt[i]) {
+			free(pkt[i]);
+			pkt[i++] = 0;
+		}
+		free(pkt);
+		pkt = 0;
+	}
+}
+#endif
+
 /* Launch threads, called at application init(). */
 int
 rte_eal_init(int argc, char **argv)
 {
+	int combined_argc; /* combine cfg and param versions */
 	int i, fctret, ret;
 	pthread_t thread_id;
 	static rte_atomic32_t run_once = RTE_ATOMIC32_INIT(0);
@@ -519,8 +544,25 @@ static void rte_eal_init_alert(const char *msg)
 
 	eal_reset_internal_config(&internal_config);
 
+#ifdef RTE_LIBRTE_CFGFILE
+	combined_argc = argc + cfg_argc;
+	char *combined_argv[combined_argc + 1];
+
+	combined_argv[0] = argv[0];
+	for (i = 0; i < cfg_argc; i++)
+		combined_argv[i + 1] = cfg_argv[i];
+	for (i = 1; i < argc; i++)
+		combined_argv[i + cfg_argc] = argv[i];
+	combined_argv[combined_argc] = NULL;
+#else
+	combined_argc = argc;
+	char **combined_argv;
+
+	combined_argv = argv;
+#endif
+
 	/* set log level as early as possible */
-	eal_log_level_parse(argc, argv);
+	eal_log_level_parse(combined_argc, combined_argv);
 
 	if (rte_eal_cpu_init() < 0) {
 		rte_eal_init_alert("Cannot detect lcores.");
@@ -528,13 +570,21 @@ static void rte_eal_init_alert(const char *msg)
 		return -1;
 	}
 
-	fctret = eal_parse_args(argc, argv);
+	fctret = eal_parse_args(combined_argc, combined_argv);
 	if (fctret < 0) {
 		rte_eal_init_alert("Invalid 'command line' arguments.");
 		rte_errno = EINVAL;
 		rte_atomic32_clear(&run_once);
 		return -1;
 	}
+	fctret -= cfg_argc;
+
+#ifdef RTE_LIBRTE_CFGFILE
+	free_pointer_array(cfg_argv);
+#endif
+
+	if (fctret)
+		argv[fctret] = argv[0];
 
 	if (internal_config.no_hugetlbfs == 0 &&
 			internal_config.process_type != RTE_PROC_SECONDARY &&
@@ -677,3 +727,77 @@ enum rte_proc_type_t
 {
 	return rte_config.process_type;
 }
+
+#ifdef RTE_LIBRTE_CFGFILE
+
+static char *strdup_with_prefix(const char *p)
+{
+	char *np;
+
+	if (strlen(p) > 1) {
+		np = (char *)malloc(strlen(p)+3);
+		if (np)
+			strcpy(np, "--");
+	} else {
+		np = (char *)malloc(strlen(p)+2);
+		if (np)
+			strcpy(np, "-");
+	}
+	return np ? strcat(np, p) : np;
+}
+
+int
+rte_eal_configure(struct rte_cfgfile *cfg)
+{
+	int i, num_entries;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	if (!rte_cfgfile_has_section(cfg, "DPDK"))
+		return 0;
+
+	num_entries = rte_cfgfile_section_num_entries(cfg, "DPDK");
+	if (num_entries <= 0)
+		return 0;
+
+	cfg_argv = malloc((num_entries * 2 + 1) * sizeof(cfg_argv[0]));
+	if (cfg_argv == NULL) {
+		rte_errno = -ENOMEM;
+		return -1;
+	}
+
+	struct rte_cfgfile_entry cfg_entries[num_entries];
+
+	rte_cfgfile_section_entries(cfg, "DPDK", cfg_entries, num_entries);
+
+	cfg_argc = 0;
+	for (i = 0; i < num_entries; i++) {
+		cfg_argv[cfg_argc] = strdup_with_prefix(cfg_entries[i].name);
+		if (!(cfg_argv[cfg_argc]))
+			goto allocation_error;
+		cfg_argc++;
+		if (strlen(cfg_entries[i].value)) {
+			cfg_argv[cfg_argc] = strdup(cfg_entries[i].value);
+			if (!(cfg_argv[cfg_argc]))
+				goto allocation_error;
+			cfg_argc++;
+		}
+	}
+	/* set last pointer to 0 */
+	cfg_argv[cfg_argc] = 0;
+
+	return cfg_argc;
+
+allocation_error:
+	rte_eal_init_alert("Cannot allocate memory in rte_eal_configure()\n");
+	rte_errno = ENOMEM;
+	for (i = 1; i < cfg_argc; i++) {
+		free(cfg_argv[i]);
+		cfg_argv[i] = 0;
+	}
+	return -1;
+}
+#endif
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 2e48a73..a9b637b 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -193,3 +193,8 @@ DPDK_17.05 {
 	vfio_get_group_no;
 
 } DPDK_17.02;
+
+DPDK_17.08 {
+	rte_eal_configure;
+} DPDK_17.05;
+
diff --git a/lib/librte_eal/common/include/rte_eal.h b/lib/librte_eal/common/include/rte_eal.h
index abf020b..6a0b18b 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -46,6 +46,8 @@
 #include <rte_per_lcore.h>
 #include <rte_config.h>
 
+struct rte_cfgfile; /* forward declaration of struct */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -188,6 +190,10 @@ struct rte_config {
  */
 int rte_eal_init(int argc, char **argv);
 
+#ifdef RTE_LIBRTE_CFGFILE
+int rte_eal_configure(struct rte_cfgfile *cfg);
+#endif
+
 /**
  * Check if a primary process is currently alive
  *
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 640afd0..656033e 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -50,6 +50,9 @@ LDLIBS += -ldl
 LDLIBS += -lpthread
 LDLIBS += -lgcc_s
 LDLIBS += -lrt
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -lrte_cfgfile
+endif
 
 # specific to linuxapp exec-env
 SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) := eal.c
diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
index 7c78f2d..b4078b5 100644
--- a/lib/librte_eal/linuxapp/eal/eal.c
+++ b/lib/librte_eal/linuxapp/eal/eal.c
@@ -78,6 +78,7 @@
 #include <rte_version.h>
 #include <rte_atomic.h>
 #include <malloc_heap.h>
+#include <rte_cfgfile.h>
 
 #include "eal_private.h"
 #include "eal_thread.h"
@@ -122,6 +123,12 @@
 /* used by rte_rdtsc() */
 int rte_cycles_vmware_tsc_map;
 
+
+static int cfg_argc;
+#ifdef RTE_LIBRTE_CFGFILE
+static char **cfg_argv;
+#endif
+
 /* Return a pointer to the configuration structure */
 struct rte_config *
 rte_eal_get_configuration(void)
@@ -745,10 +752,12 @@ static void rte_eal_init_alert(const char *msg)
 	RTE_LOG(ERR, EAL, "%s\n", msg);
 }
 
+
 /* Launch threads, called at application init(). */
 int
 rte_eal_init(int argc, char **argv)
 {
+	int combined_argc; /* combine cfg and param versions */
 	int i, fctret, ret;
 	pthread_t thread_id;
 	static rte_atomic32_t run_once = RTE_ATOMIC32_INIT(0);
@@ -776,8 +785,25 @@ static void rte_eal_init_alert(const char *msg)
 
 	eal_reset_internal_config(&internal_config);
 
+#ifdef RTE_LIBRTE_CFGFILE
+	combined_argc = argc + cfg_argc;
+	char *combined_argv[combined_argc + 1];
+
+	combined_argv[0] = argv[0];
+	for (i = 0; i < cfg_argc; i++)
+		combined_argv[i + 1] = cfg_argv[i];
+	for (i = 1; i < argc; i++)
+		combined_argv[i + cfg_argc] = argv[i];
+	combined_argv[combined_argc] = NULL;
+#else
+	combined_argc = argc;
+	char **combined_argv;
+
+	combined_argv = argv;
+#endif
+
 	/* set log level as early as possible */
-	eal_log_level_parse(argc, argv);
+	eal_log_level_parse(combined_argc, combined_argv);
 
 	if (rte_eal_cpu_init() < 0) {
 		rte_eal_init_alert("Cannot detect lcores.");
@@ -785,13 +811,17 @@ static void rte_eal_init_alert(const char *msg)
 		return -1;
 	}
 
-	fctret = eal_parse_args(argc, argv);
+	fctret = eal_parse_args(combined_argc, combined_argv);
 	if (fctret < 0) {
 		rte_eal_init_alert("Invalid 'command line' arguments.");
 		rte_errno = EINVAL;
 		rte_atomic32_clear(&run_once);
 		return -1;
 	}
+	fctret -= cfg_argc;
+
+	if (fctret)
+		argv[fctret] = argv[0];
 
 	if (internal_config.no_hugetlbfs == 0 &&
 			internal_config.process_type != RTE_PROC_SECONDARY &&
@@ -995,3 +1025,77 @@ int rte_eal_has_hugepages(void)
 	/* Module has been found */
 	return 1;
 }
+
+#ifdef RTE_LIBRTE_CFGFILE
+
+static char *strdup_with_prefix(const char *p)
+{
+	char *np;
+
+	if (strlen(p) > 1) {
+		np = (char *)malloc(strlen(p)+3);
+		if (np)
+			strcpy(np, "--");
+	} else {
+		np = (char *)malloc(strlen(p)+2);
+		if (np)
+			strcpy(np, "-");
+	}
+	return np ? strcat(np, p) : np;
+}
+
+int
+rte_eal_configure(struct rte_cfgfile *cfg)
+{
+	int i, num_entries;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	if (!rte_cfgfile_has_section(cfg, "DPDK"))
+		return 0;
+
+	num_entries = rte_cfgfile_section_num_entries(cfg, "DPDK");
+	if (num_entries <= 0)
+		return 0;
+
+	cfg_argv = malloc((num_entries * 2 + 1) * sizeof(cfg_argv[0]));
+	if (cfg_argv == NULL) {
+		rte_errno = -ENOMEM;
+		return -1;
+	}
+
+	struct rte_cfgfile_entry cfg_entries[num_entries];
+
+	rte_cfgfile_section_entries(cfg, "DPDK", cfg_entries, num_entries);
+
+	cfg_argc = 0;
+	for (i = 0; i < num_entries; i++) {
+		cfg_argv[cfg_argc] = strdup_with_prefix(cfg_entries[i].name);
+		if (!(cfg_argv[cfg_argc]))
+			goto allocation_error;
+		cfg_argc++;
+		if (strlen(cfg_entries[i].value)) {
+			cfg_argv[cfg_argc] = strdup(cfg_entries[i].value);
+			if (!(cfg_argv[cfg_argc]))
+				goto allocation_error;
+			cfg_argc++;
+		}
+	}
+	/* set last pointer to 0 */
+	cfg_argv[cfg_argc] = 0;
+
+	return cfg_argc;
+
+allocation_error:
+	rte_eal_init_alert("Cannot allocate memory in rte_eal_configure()\n");
+	rte_errno = ENOMEM;
+	for (i = 1; i < cfg_argc; i++) {
+		free(cfg_argv[i]);
+		cfg_argv[i] = 0;
+	}
+	return -1;
+}
+#endif
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 670bab3..c93e6d9 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -198,3 +198,7 @@ DPDK_17.05 {
 	vfio_get_group_no;
 
 } DPDK_17.02;
+
+DPDK_17.08 {
+	rte_eal_configure;
+} DPDK_17.05;
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index bcaf1b3..642af92 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,7 +80,6 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_POWER)          += -lrte_power
 
 _LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER)          += -lrte_timer
 _LDLIBS-$(CONFIG_RTE_LIBRTE_EFD)            += -lrte_efd
-_LDLIBS-$(CONFIG_RTE_LIBRTE_CFGFILE)        += -lrte_cfgfile
 
 _LDLIBS-y += --whole-archive
 
@@ -96,6 +95,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL)        += -lrte_mempool
 _LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING)   += -lrte_mempool_ring
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RING)           += -lrte_ring
 _LDLIBS-$(CONFIG_RTE_LIBRTE_EAL)            += -lrte_eal
+_LDLIBS-$(CONFIG_RTE_LIBRTE_CFGFILE)        += -lrte_cfgfile
 _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline
 _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH 3/3] app/testpmd: changed example to parse options from cfg file
  2017-05-30  8:30 [PATCH 0/3] Add support for using a config file for DPDK Jacek Piasecki
  2017-05-30  8:30 ` [PATCH 1/3] cfgfile: add new API functions Jacek Piasecki
  2017-05-30  8:30 ` [PATCH 2/3] eal: add functions parsing EAL arguments Jacek Piasecki
@ 2017-05-30  8:30 ` Jacek Piasecki
  2017-06-20  2:13   ` Wu, Jingjing
  2 siblings, 1 reply; 70+ messages in thread
From: Jacek Piasecki @ 2017-05-30  8:30 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, michalx.k.jastrzebski, Kuba Kozak

From: Kuba Kozak <kubax.kozak@intel.com>

This patch shows how to pass arguments to application
using config.ini file.

If a --cfgfile-path <path> option is passed into commandline
non EAL section, then the file is loaded and used by app.

If a file called config.ini is present in current working
directory, and no --cfgfile-path option is passed in, config.ini
file will be loaded and used by app.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test-pmd/config.ini   |   16 ++++++
 app/test-pmd/parameters.c |  135 +++++++++++++++++++++++++++++++++++++++++++--
 app/test-pmd/testpmd.c    |   51 ++++++++++++++++-
 app/test-pmd/testpmd.h    |    2 +-
 4 files changed, 196 insertions(+), 8 deletions(-)
 create mode 100644 app/test-pmd/config.ini

diff --git a/app/test-pmd/config.ini b/app/test-pmd/config.ini
new file mode 100644
index 0000000..dfafc55
--- /dev/null
+++ b/app/test-pmd/config.ini
@@ -0,0 +1,16 @@
+[DPDK]
+v =
+l = 0-4
+n = 4
+master-lcore = 0
+proc-type = primary
+vdev = net_ring0
+vdev = net_ring1
+vdev = net_ring2
+vdev = net_ring3
+
+[TEST-PMD]
+i =
+portmask = 0xf
+nb-cores = 4
+port-topology = paired
diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index fbe6284..974c548 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -77,9 +77,15 @@
 #include <rte_eth_bond.h>
 #endif
 #include <rte_flow.h>
+#include <rte_cfgfile.h>
 
 #include "testpmd.h"
 
+#ifdef RTE_LIBRTE_CFGFILE
+static int cfg_argc;
+static char **cfg_argv;
+#endif
+
 static void
 usage(char* progname)
 {
@@ -549,15 +555,107 @@
 	return 0;
 }
 
+#ifdef RTE_LIBRTE_CFGFILE
+
+static void
+free_pointer_array(char **pkt)
+{
+	int i = 0;
+
+	if (pkt) {
+		while (pkt[i]) {
+			free(pkt[i]);
+			pkt[i++] = 0;
+		}
+		free(pkt);
+		pkt = 0;
+	}
+}
+
+static char *strdup_with_prefix(const char *p)
+{
+	char *np;
+
+	if (strlen(p) > 1) {
+		np = (char *)malloc(strlen(p)+3);
+		if (np)
+			strcpy(np, "--");
+	} else {
+		np = (char *)malloc(strlen(p)+2);
+		if (np)
+			strcpy(np, "-");
+	}
+	return np ? strcat(np, p) : np;
+}
+
+#define APP_SECTION "TEST-PMD"
+static int non_eal_configure(struct rte_cfgfile *cfg)
+{
+	int i, num_entries;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	if (!rte_cfgfile_has_section(cfg, APP_SECTION))
+		return 0;
+
+	num_entries = rte_cfgfile_section_num_entries(cfg, APP_SECTION);
+	if (num_entries <= 0)
+		return 0;
+
+	cfg_argv = malloc((num_entries * 2 + 1) * sizeof(cfg_argv[0]));
+	if (cfg_argv == NULL) {
+		rte_errno = -ENOMEM;
+		return -1;
+	}
+
+	struct rte_cfgfile_entry cfg_entries[num_entries];
+
+	rte_cfgfile_section_entries(cfg, APP_SECTION, cfg_entries, num_entries);
+
+	cfg_argc = 0;
+	for (i = 0; i < num_entries; i++) {
+		cfg_argv[cfg_argc] = strdup_with_prefix(cfg_entries[i].name);
+		if (!(cfg_argv[cfg_argc]))
+			goto allocation_error;
+		cfg_argc++;
+		if (strlen(cfg_entries[i].value)) {
+			cfg_argv[cfg_argc] = strdup(cfg_entries[i].value);
+			if (!(cfg_argv[cfg_argc]))
+				goto allocation_error;
+			cfg_argc++;
+		}
+	}
+	/* set last pointer to 0 */
+	cfg_argv[cfg_argc] = 0;
+
+	return cfg_argc;
+
+allocation_error:
+	printf("Warning: Cannot allocate memory in rte_eal_configure()\n");
+	rte_errno = ENOMEM;
+	for (i = 1; i < cfg_argc; i++) {
+		free(cfg_argv[i]);
+		cfg_argv[i] = 0;
+	}
+	return -1;
+}
+#endif
+
 void
-launch_args_parse(int argc, char** argv)
+launch_args_parse(int argc, char **argv, struct rte_cfgfile *cfg)
 {
+	int combined_argc; /* combine cfg and param versions */
+	char **combined_argv;
 	int n, opt;
 	char **argvopt;
 	int opt_idx;
 	enum { TX, RX };
 
 	static struct option lgopts[] = {
+		{ "cfgfile-path",		1, 0, 1 },
 		{ "help",			0, 0, 0 },
 #ifdef RTE_LIBRTE_CMDLINE
 		{ "interactive",		0, 0, 0 },
@@ -632,14 +730,32 @@
 		{ 0, 0, 0, 0 },
 	};
 
-	argvopt = argv;
+#ifdef RTE_LIBRTE_CFGFILE
+	int i;
+
+	non_eal_configure(cfg);
+
+	combined_argc = argc + cfg_argc;
+	combined_argv = malloc((combined_argc + 1) * sizeof(char *));
+	combined_argv[0] = argv[0];
+	for (i = 0; i < cfg_argc; i++)
+		combined_argv[i + 1] = cfg_argv[i];
+	for (i = 1; i < argc; i++)
+		combined_argv[i + cfg_argc] = argv[i];
+	combined_argv[combined_argc] = NULL;
+#else
+	combined_argc = argc;
+	combined_argv = argv;
+#endif
+
+	argvopt = combined_argv;
 
 #ifdef RTE_LIBRTE_CMDLINE
 #define SHORTOPTS "i"
 #else
 #define SHORTOPTS ""
 #endif
-	while ((opt = getopt_long(argc, argvopt, SHORTOPTS "ah",
+	while ((opt = getopt_long(combined_argc, argvopt, SHORTOPTS "ah",
 				 lgopts, &opt_idx)) != EOF) {
 		switch (opt) {
 #ifdef RTE_LIBRTE_CMDLINE
@@ -655,7 +771,7 @@
 
 		case 0: /*long options */
 			if (!strcmp(lgopts[opt_idx].name, "help")) {
-				usage(argv[0]);
+				usage(combined_argv[0]);
 				rte_exit(EXIT_SUCCESS, "Displayed help\n");
 			}
 #ifdef RTE_LIBRTE_CMDLINE
@@ -1094,14 +1210,21 @@
 
 			break;
 		case 'h':
-			usage(argv[0]);
+			usage(combined_argv[0]);
 			rte_exit(EXIT_SUCCESS, "Displayed help\n");
 			break;
+		case 1:
+			/* does nothing*/
+			break;
 		default:
-			usage(argv[0]);
+			usage(combined_argv[0]);
 			rte_exit(EXIT_FAILURE,
 				 "Command line is incomplete or incorrect\n");
 			break;
 		}
 	}
+
+#ifdef RTE_LIBRTE_CFGFILE
+	free_pointer_array(cfg_argv);
+#endif
 }
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index d1041af..0d9d363 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -87,6 +87,7 @@
 #ifdef RTE_LIBRTE_LATENCY_STATS
 #include <rte_latencystats.h>
 #endif
+#include <rte_cfgfile.h>
 
 #include "testpmd.h"
 
@@ -2248,15 +2249,54 @@ uint8_t port_is_bonding_slave(portid_t slave_pid)
 	}
 }
 
+
+
+#ifdef RTE_LIBRTE_CFGFILE
+/* Load config file path from command line */
+static char *
+cfgfile_load_path(int argc, char **argv)
+{
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		if (!strcmp("--cfgfile-path", argv[i])) {
+			if (i < argc)
+				return strdup(argv[i+1]);
+		}
+	}
+	return 0;
+}
+#endif
+
 int
 main(int argc, char** argv)
 {
 	int  diag;
 	uint8_t port_id;
+	struct rte_cfgfile *cfg = NULL;
+	char *config_file = NULL;
 
 	signal(SIGINT, signal_handler);
 	signal(SIGTERM, signal_handler);
 
+#ifdef RTE_LIBRTE_CFGFILE
+	/* load --cfgfile-path argument from argv */
+	config_file = cfgfile_load_path(argc, argv);
+
+	if (config_file) {
+		printf("Info: found cfgfile-path \"%s\"\n", config_file);
+	} else {
+		printf("Info: not found cfgfile-path parameter "
+				"(searching for cfgfile "
+				"in default directory)\n");
+		config_file = strdup("config.ini");
+	}
+	cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+	if (cfg == NULL)
+		printf("Info: Valid cfgfile not found\n");
+	rte_eal_configure(cfg);
+#endif
+
 	diag = rte_eal_init(argc, argv);
 	if (diag < 0)
 		rte_panic("Cannot init EAL\n");
@@ -2289,7 +2329,16 @@ uint8_t port_is_bonding_slave(portid_t slave_pid)
 	argc -= diag;
 	argv += diag;
 	if (argc > 1)
-		launch_args_parse(argc, argv);
+		launch_args_parse(argc, argv, cfg);
+
+
+	rte_cfgfile_close(cfg);
+	cfg = 0;
+
+	if (config_file) {
+		free(config_file);
+		config_file = 0;
+	}
 
 	if (!nb_rxq && !nb_txq)
 		printf("Warning: Either rx or tx queues should be non-zero\n");
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index e6c43ba..5c1d477 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -499,7 +499,7 @@ enum tx_pkt_split {
 unsigned int parse_item_list(char* str, const char* item_name,
 			unsigned int max_items,
 			unsigned int *parsed_items, int check_unique_values);
-void launch_args_parse(int argc, char** argv);
+void launch_args_parse(int argc, char **argv, struct rte_cfgfile *cfg);
 void cmdline_read_from_file(const char *filename);
 void prompt(void);
 void prompt_exit(void);
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [PATCH 1/3] cfgfile: add new API functions
  2017-05-30  8:30 ` [PATCH 1/3] cfgfile: add new API functions Jacek Piasecki
@ 2017-05-31 14:20   ` Bruce Richardson
  2017-05-31 14:22     ` Bruce Richardson
  2017-06-26 10:59   ` [PATCH v2 0/7] Add support for using a config file for DPDK Jacek Piasecki
  1 sibling, 1 reply; 70+ messages in thread
From: Bruce Richardson @ 2017-05-31 14:20 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain, michalx.k.jastrzebski

On Tue, May 30, 2017 at 10:30:35AM +0200, Jacek Piasecki wrote:
> Extend existing cfgfile library with providing new API functions:
> rte_cfgfile_create() - create new cfgfile object
> rte_cfgfile_add_section() - add new section to existing cfgfile object
> rte_cfgfile_add_entry() - add new entry to existing cfgfile object
> in specified section
> This modification allows to create a cfgfile on runtime and
> opens up the possibility to have applications dynamically build up
> a proper DPDK configuration, rather than having to have a pre-existing one.
> 
> Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>

Hi,

There seems to be some additional work in this patch apart from what is
described above, which may go into a separate patchset to improve
readability. For example, the dependency on librte_eal (apart from one
header) is removed, so those changes could go in a separate patch.
Also, you might consider moving the rework of the load API to a separate
patch, i.e. have one patch add the new APIs, and then a second patch
rework existing load code to be simpler using those APIs.

Other comments inline below.

/Bruce

> ---
>  lib/librte_cfgfile/Makefile                |    1 +
>  lib/librte_cfgfile/rte_cfgfile.c           |  293 ++++++++++++++++------------
>  lib/librte_cfgfile/rte_cfgfile.h           |   50 +++++
>  lib/librte_cfgfile/rte_cfgfile_version.map |    9 +
>  4 files changed, 227 insertions(+), 126 deletions(-)
>

<snip>

>  
> +struct rte_cfgfile *
> +rte_cfgfile_create(int flags)
> +{
> +	int allocated_sections = CFG_ALLOC_SECTION_BATCH;
> +	struct rte_cfgfile *cfg = NULL;
> +
> +	cfg = malloc(sizeof(*cfg) + sizeof(cfg->sections[0]) *
> +			allocated_sections);
> +	if (cfg == NULL) {
> +		printf("Error - no memory to create cfgfile\n");
> +		return NULL;
> +	}
> +
> +	memset(cfg->sections, 0, sizeof(cfg->sections[0]) * allocated_sections);
> +
> +	cfg->flags = flags;
> +	cfg->num_sections = 0;
> +
> +	if (flags & CFG_FLAG_GLOBAL_SECTION)
> +		rte_cfgfile_add_section(&cfg, "GLOBAL");
> +	return cfg;
> +}
> +
> +int
> +rte_cfgfile_add_section(struct rte_cfgfile **cfgfile, const char *sectionname)
> +{
> +	struct rte_cfgfile *cfg = *cfgfile;
> +	int curr_section = cfg->num_sections - 1;

Consider removing this variable, I don't know if it's neccessary. It
certainly shouldn't be assigned here using "- 1" and then incremented
unconditionally later on. Throughout the function cfg->num_sections can
be used instead.

> +	int allocated_sections = 0;
> +
> +	/* check if given section already exist */
> +	if (rte_cfgfile_has_section(cfg, sectionname) != 0) {
> +		printf("Given section '%s' already exist\n", sectionname);
> +		return 0;

I think this should return an error code, -EEXIST.
However, I believe we have support for multiple sections with the same
name - see rte_cfgfile_section_entries_by_entries - so this check will
break that support.

> +	}
> +	/* calculate allocated sections from number of sections */
> +	if ((cfg->num_sections) != 0)
> +		allocated_sections = (cfg->num_sections/
> +			CFG_ALLOC_SECTION_BATCH + 1) * CFG_ALLOC_SECTION_BATCH;
> +

Rather than compute this value each time, just add a field to the
structure indicating how many are allocated. It should be less error
prone.

> +	curr_section++;
> +	/* resize overall struct if we don't have room for more	sections */
> +	if (curr_section == allocated_sections) {
> +		allocated_sections += CFG_ALLOC_SECTION_BATCH;
> +		struct rte_cfgfile *n_cfg = realloc(cfg,
> +			sizeof(*cfg) + sizeof(cfg->sections[0])
> +			* allocated_sections);
> +		if (n_cfg == NULL)
> +			return -ENOMEM;
> +		*cfgfile = n_cfg;
> +		cfg = *cfgfile;

We should change the definition of rte_config structure to have a
pointer to the sections, rather than having them tacked on at the end.
This would mean that you could just realloc the sections themselves, not
the whole structure. It therefore means that the function can take a
regular struct ptr param, rather than a ptr to struct ptr.

> +	}
> +	/* allocate space for new section */
> +	cfg->sections[curr_section] = malloc(
> +		sizeof(*cfg->sections[0]) +
> +		sizeof(cfg->sections[0]->entries[0]) *
> +		CFG_ALLOC_ENTRY_BATCH);
> +
> +	if (cfg->sections[curr_section] == NULL)
> +		return -ENOMEM;
> +
> +	snprintf(cfg->sections[curr_section]->name,
> +			sizeof(cfg->sections[0]->name), "%s", sectionname);
> +
> +	cfg->sections[curr_section]->num_entries = 0;
> +	cfg->num_sections = curr_section + 1;

Don't need to use curr_section here, just do cfg->num_sections++;
cfg->num_sections++;

> +	return 0;
> +}
> +
> +static int
> +_get_section_index(struct rte_cfgfile *cfgfile, const char *sectionname) {
> +	int i;
> +
> +	for (i = 0; i < cfgfile->num_sections; i++) {
> +		if (strncmp(cfgfile->sections[i]->name, sectionname,
> +				sizeof(cfgfile->sections[0]->name)) == 0)
> +			return i;
> +	}
> +	return -1;
> +}

This shouldn't be necessary, as we can use existing _get_section()
function instead. [Or else make one use the other]

> +
> +static signed int
> +_add_entry(struct rte_cfgfile *cfgfile, const signed int curr_section,
> +		const char *entryname, const char *entryvalue)
> +{
> +	int allocated_entries = 0;
> +	int curr_entry = cfgfile->sections[curr_section]->num_entries - 1;
> +
> +	/* calculate allocated entries from number of entries */
> +	if ((curr_entry + 1) != 0)
> +		allocated_entries = ((curr_entry + 1)/
> +			CFG_ALLOC_ENTRY_BATCH + 1) * CFG_ALLOC_ENTRY_BATCH;
> +

As with the add_section, we don't need a curr_entry variable, and also
add a new struct member to track the number of allocated elements.

> +	curr_entry++;
> +	struct rte_cfgfile_section *sect = cfgfile->sections[curr_section];
> +
> +	sect->entries[curr_entry] = malloc(sizeof(*sect->entries[0]));
> +
> +	if (curr_entry == allocated_entries) {
> +		allocated_entries += CFG_ALLOC_ENTRY_BATCH;
> +		struct rte_cfgfile_section *n_sect = realloc(
> +			sect, sizeof(*sect) +
> +			sizeof(sect->entries[0]) *
> +			allocated_entries);
> +		if (n_sect == NULL)
> +			return -ENOMEM;
> +		sect = cfgfile->sections[curr_section] = n_sect;
> +	}
> +
> +	if (sect->entries[curr_entry] == NULL) {
> +		cfgfile->num_sections = curr_section + 1;
> +		if (curr_section >= 0) {
> +			cfgfile->sections[curr_section]->num_entries =
> +								curr_entry + 1;
> +			return -ENOMEM;
> +		}
> +	}
> +

Do we need to check for the existence of an entry with that name here?

> +	struct rte_cfgfile_entry *entry = sect->entries[curr_entry];
> +
> +	snprintf(entry->name, sizeof(entry->name), "%s", entryname);
> +	snprintf(entry->value, sizeof(entry->value), "%s", entryvalue);
> +	_strip(entry->name, strnlen(entry->name, sizeof(entry->name)));
> +	_strip(entry->value, strnlen(entry->value, sizeof(entry->value)));
> +
> +	cfgfile->sections[curr_section]->num_entries = curr_entry + 1;
> +
> +	return 0;
> +};
> +
> +int rte_cfgfile_add_entry(struct rte_cfgfile *cfgfile, const char *sectionname,
> +		const char *entryname, const char *entryvalue)
> +{
> +	int curr_section;
> +
> +	/* check if given entry in specified section already exist */
> +	if (rte_cfgfile_has_entry(cfgfile, sectionname, entryname) != 0)
> +		return 0;
> +
> +	/* search for section index by sectionname */
> +	curr_section = _get_section_index(cfgfile, sectionname);
> +
> +	if (curr_section < 0)
> +		return -EINVAL;
> +
> +	return _add_entry(cfgfile, curr_section, entryname, entryvalue);

rename function to "add_section_entry", perhaps. If you use
_get_section() function instead of the _get_section_index() one, you can
potentially pass in the section by pointer and not pass in the cfgfile
pointer at all.

> +}
>  
<snip>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 1/3] cfgfile: add new API functions
  2017-05-31 14:20   ` Bruce Richardson
@ 2017-05-31 14:22     ` Bruce Richardson
  0 siblings, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-05-31 14:22 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain, michalx.k.jastrzebski

On Wed, May 31, 2017 at 03:20:00PM +0100, Bruce Richardson wrote:
> On Tue, May 30, 2017 at 10:30:35AM +0200, Jacek Piasecki wrote:
> > Extend existing cfgfile library with providing new API functions:
> > rte_cfgfile_create() - create new cfgfile object
> > rte_cfgfile_add_section() - add new section to existing cfgfile object
> > rte_cfgfile_add_entry() - add new entry to existing cfgfile object
> > in specified section
> > This modification allows to create a cfgfile on runtime and
> > opens up the possibility to have applications dynamically build up
> > a proper DPDK configuration, rather than having to have a pre-existing one.
> > 
> > Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
> 
> Hi,
> 
> There seems to be some additional work in this patch apart from what is
> described above, which may go into a separate patchset to improve
> readability.

s/a separate patchset/separate patches/

> For example, the dependency on librte_eal (apart from one
> header) is removed, so those changes could go in a separate patch.
> Also, you might consider moving the rework of the load API to a separate
> patch, i.e. have one patch add the new APIs, and then a second patch
> rework existing load code to be simpler using those APIs.
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 2/3] eal: add functions parsing EAL arguments
  2017-05-30  8:30 ` [PATCH 2/3] eal: add functions parsing EAL arguments Jacek Piasecki
@ 2017-05-31 15:46   ` Bruce Richardson
  0 siblings, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-05-31 15:46 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain, michalx.k.jastrzebski, Kuba Kozak

On Tue, May 30, 2017 at 10:30:36AM +0200, Jacek Piasecki wrote:
> From: Kuba Kozak <kubax.kozak@intel.com>
> 
> added function rte_eal_configure which translate
> options from config file into argc, **argv form.
> 
> changed function rte_eal_init to meld
> argc, argv** options from config file with
> these from command line and then parse as
> before
> 

Hi,

while this approach to merging the values from two sources (config
struct and cmdline) works, I'm not sure it's the best way to implement
this. I would have thought it more logical to separate out cmdline args
into name-value pairs to add to the cfgfile struct internally instead.
However, this has the disadvantage of forcing a dependency on cfgfile,
or adding a third name-value pair struct to DPDK [after kvargs, which
supports flat pairs, and cfgfile, which supports sections and pairs].
Using the cfgfile struct also saves us from "flattening out"
hierarchical information we may wish to add in the config file. For
example, consider the following cfgfile structure:

[DPDK]
lcores = 20-25
socket-mem = 2048, 1024
vdevs = 2

[DPDK.vdev0]
name = eth_pcap0
rx_pcap = /path/to/file
tx_pcap = /path/to/other_file

[DPDK.vdev1]
name = eth_tap0
iface = iface_arg

Longer term, it would be good to keep those vdev entries separated as passed
as such to the driver, rather than merging them back into a single
string to be separated out again by kvargs lib.

/Bruce

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/3] app/testpmd: changed example to parse options from cfg file
  2017-05-30  8:30 ` [PATCH 3/3] app/testpmd: changed example to parse options from cfg file Jacek Piasecki
@ 2017-06-20  2:13   ` Wu, Jingjing
  2017-06-20 10:10     ` Kozak, KubaX
  0 siblings, 1 reply; 70+ messages in thread
From: Wu, Jingjing @ 2017-06-20  2:13 UTC (permalink / raw)
  To: Piasecki, JacekX, dev
  Cc: Richardson, Bruce, Jain, Deepak K, Jastrzebski, MichalX K, Kozak, KubaX

> +
> +#ifdef RTE_LIBRTE_CFGFILE
> +/* Load config file path from command line */ static char *
> +cfgfile_load_path(int argc, char **argv) {
> +	int i;
> +
> +	for (i = 0; i < argc; i++) {
> +		if (!strcmp("--cfgfile-path", argv[i])) {
> +			if (i < argc)
> +				return strdup(argv[i+1]);
> +		}
> +	}
> +	return 0;
> +}
> +#endif
> +
It is a little confused. Is the cfgfile-path is application's argument or EAL's?
Where is it supposed to be located?

>  int
>  main(int argc, char** argv)
>  {
>  	int  diag;
>  	uint8_t port_id;
> +	struct rte_cfgfile *cfg = NULL;
> +	char *config_file = NULL;
> 
>  	signal(SIGINT, signal_handler);
>  	signal(SIGTERM, signal_handler);
> 
> +#ifdef RTE_LIBRTE_CFGFILE
> +	/* load --cfgfile-path argument from argv */
> +	config_file = cfgfile_load_path(argc, argv);
> +
> +	if (config_file) {
> +		printf("Info: found cfgfile-path \"%s\"\n", config_file);
> +	} else {
> +		printf("Info: not found cfgfile-path parameter "
> +				"(searching for cfgfile "
> +				"in default directory)\n");
> +		config_file = strdup("config.ini");
> +	}
> +	cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
> +	if (cfg == NULL)
> +		printf("Info: Valid cfgfile not found\n");
> +	rte_eal_configure(cfg);
> +#endif
> +
Does it mean if RTE_LIBRTE_CFGFILE is defined, then the arguments are passed by
Cfgfile, if no cfgfile will use config.ini by default? How about the legacy command lines?
I think even cfgfile is good, we still need to keep the command line way.

>  	diag = rte_eal_init(argc, argv);
>  	if (diag < 0)
>  		rte_panic("Cannot init EAL\n");
> @@ -2289,7 +2329,16 @@ uint8_t port_is_bonding_slave(portid_t slave_pid)
>  	argc -= diag;
>  	argv += diag;
>  	if (argc > 1)
> -		launch_args_parse(argc, argv);
> +		launch_args_parse(argc, argv, cfg);
> +

The argc and argv have been overwritten by cfgfile, right? Does it make sense to
Pass cfg to  launch_args_parse?


And you also need to update the testpmd doc to describe this new use.

Thanks
Jingjing

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH 3/3] app/testpmd: changed example to parse options from cfg file
  2017-06-20  2:13   ` Wu, Jingjing
@ 2017-06-20 10:10     ` Kozak, KubaX
  0 siblings, 0 replies; 70+ messages in thread
From: Kozak, KubaX @ 2017-06-20 10:10 UTC (permalink / raw)
  To: Wu, Jingjing, dev
  Cc: Richardson, Bruce, Jain, Deepak K, Jastrzebski, MichalX K,
	Piasecki, JacekX

Comments inline.

Best regards,
Kuba Kozak

> -----Original Message-----
> From: Wu, Jingjing
> Sent: Tuesday, June 20, 2017 04:14
> To: Piasecki, JacekX <jacekx.piasecki@intel.com>; dev@dpdk.org
> Cc: Richardson, Bruce <bruce.richardson@intel.com>; Jain, Deepak K <deepak.k.jain@intel.com>;
> Jastrzebski, MichalX K <michalx.k.jastrzebski@intel.com>; Kozak, KubaX <kubax.kozak@intel.com>
> Subject: RE: [dpdk-dev] [PATCH 3/3] app/testpmd: changed example to parse options from cfg file
> 
> > +
> > +#ifdef RTE_LIBRTE_CFGFILE
> > +/* Load config file path from command line */ static char *
> > +cfgfile_load_path(int argc, char **argv) {
> > +	int i;
> > +
> > +	for (i = 0; i < argc; i++) {
> > +		if (!strcmp("--cfgfile-path", argv[i])) {
> > +			if (i < argc)
> > +				return strdup(argv[i+1]);
> > +		}
> > +	}
> > +	return 0;
> > +}
> > +#endif
> > +
> It is a little confused. Is the cfgfile-path is application's argument or EAL's?
> Where is it supposed to be located?

It is an application argument.

> 
> >  int
> >  main(int argc, char** argv)
> >  {
> >  	int  diag;
> >  	uint8_t port_id;
> > +	struct rte_cfgfile *cfg = NULL;
> > +	char *config_file = NULL;
> >
> >  	signal(SIGINT, signal_handler);
> >  	signal(SIGTERM, signal_handler);
> >
> > +#ifdef RTE_LIBRTE_CFGFILE
> > +	/* load --cfgfile-path argument from argv */
> > +	config_file = cfgfile_load_path(argc, argv);
> > +
> > +	if (config_file) {
> > +		printf("Info: found cfgfile-path \"%s\"\n", config_file);
> > +	} else {
> > +		printf("Info: not found cfgfile-path parameter "
> > +				"(searching for cfgfile "
> > +				"in default directory)\n");
> > +		config_file = strdup("config.ini");
> > +	}
> > +	cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
> > +	if (cfg == NULL)
> > +		printf("Info: Valid cfgfile not found\n");
> > +	rte_eal_configure(cfg);
> > +#endif
> > +
> Does it mean if RTE_LIBRTE_CFGFILE is defined, then the arguments are passed by Cfgfile, if no cfgfile
> will use config.ini by default? How about the legacy command lines?
> I think even cfgfile is good, we still need to keep the command line way.

If RTE_LIBRTE_CFGFILE is defined then arguments are passed both by cfgfile and command line. Command line arguments have priority over cfgfile arguments and they override their values.

> 
> >  	diag = rte_eal_init(argc, argv);
> >  	if (diag < 0)
> >  		rte_panic("Cannot init EAL\n");
> > @@ -2289,7 +2329,16 @@ uint8_t port_is_bonding_slave(portid_t slave_pid)
> >  	argc -= diag;
> >  	argv += diag;
> >  	if (argc > 1)
> > -		launch_args_parse(argc, argv);
> > +		launch_args_parse(argc, argv, cfg);
> > +
> 
> The argc and argv have been overwritten by cfgfile, right? Does it make sense to Pass cfg to
> launch_args_parse?

This function translate argv arguments into configuration file form and append/override current cfgfile.
Finally it parse application arguments from cfgfile. 
Intention was to parse arguments from single source (configuration file form).

> 
> 
> And you also need to update the testpmd doc to describe this new use.
> 
> Thanks
> Jingjing

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v2 0/7] Add support for using a config file for DPDK
  2017-05-30  8:30 ` [PATCH 1/3] cfgfile: add new API functions Jacek Piasecki
  2017-05-31 14:20   ` Bruce Richardson
@ 2017-06-26 10:59   ` Jacek Piasecki
  2017-06-26 10:59     ` [PATCH v2 1/7] cfgfile: remove EAL dependency Jacek Piasecki
                       ` (6 more replies)
  1 sibling, 7 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-26 10:59 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Jacek Piasecki

This patchset introduce a mechanism for running dpdk application with parameters
provided by configuration file. New API for cfgfile library allows to create
a cfgfile at runtime, add new section and add entry in a section - opens up
the possibility to have applications dynamically build up a proper
DPDK configuration, rather than having to have a pre-existing one.

A new API for EAL takes a config file data type - either loaded from file,
or built up programmatically in the application - and extracts DPDK parameters
from it to be used when eal init is called. This allows apps to have
an alternative method to configure EAL, other than via command-line parameters.

Testpmd application is used to the demonstrate the new eal API.
If a --cfgfile-path <path> option is passed into command line non EAL section,
then the file is loaded and used by app. If a file called config.ini
is present in current working directory, and no --cfgfile-path option is
passed in, config.ini file will be loaded and used by app.

Last patch demonstrates the usage of JSON file format insted of config.ini
JSON file can be called the same way as above, thru --cfgfile-path <path>

---
v2:
  lib eal:
  	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
	Now this function load data from cfg structure and did initial
	initialization of EAL arguments. Vdev argument are stored in different
	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
	function it is necessary to call rte_eal_init to complete EAL
	initialization. There is no more merging arguments from different
	sources (cfg file and command line).
  	Added non_eal_configure to testpmd application.
	Function maintain the same functionality as rte_eal_configure but
	for non-eal arguments. 
  	Added config JSON feature to testpmd last patch from patchset contain
	example showing use of .json configuration files.

  lib cfgfile:
  	Rework of add_section(), add_entry() new implementation
  	New members allocated_entries/sections, free_entries/sections
	in rte_cfgfile structure, change in array of pointers
	**sections, **entries instead of *sections[], *entries[]
  	Add  set_entry() to update/overwrite already existing entry in cfgfile
	struct
  	Add save() function to save on disc cfgfile structure in INI format
  	Rework of existing load() function  simplifying the code
  	Add unit test realloc_sections() in TEST app for testing realloc/malloc
	of new API functions, add test for save() function

Jacek Piasecki (3):
  cfgfile: remove EAL dependency
  cfgfile: add new functions to API
  test/cfgfile: add new unit test

Kuba Kozak (4):
  cfgfile: rework of load function
  eal: add functions parsing EAL arguments
  app/testpmd: changed example to parse options from cfg file
  app/testpmd: add parse arguments from JSON config file

 app/test-pmd/Makefile                            |    6 +
 app/test-pmd/config.ini                          |   24 +
 app/test-pmd/config.json                         |   33 +
 app/test-pmd/parameters.c                        | 1193 +++++++++++++---------
 app/test-pmd/testpmd.c                           |  159 ++-
 app/test-pmd/testpmd.h                           |    3 +-
 config/common_base                               |    6 +
 lib/Makefile                                     |    6 +-
 lib/librte_cfgfile/Makefile                      |    1 +
 lib/librte_cfgfile/rte_cfgfile.c                 |  397 ++++---
 lib/librte_cfgfile/rte_cfgfile.h                 |   76 ++
 lib/librte_cfgfile/rte_cfgfile_version.map       |   11 +
 lib/librte_eal/bsdapp/eal/Makefile               |    4 +
 lib/librte_eal/bsdapp/eal/eal.c                  |  249 ++++-
 lib/librte_eal/bsdapp/eal/rte_eal_version.map    |    4 +
 lib/librte_eal/common/eal_common_cpuflags.c      |   14 +-
 lib/librte_eal/common/eal_common_lcore.c         |   11 +-
 lib/librte_eal/common/eal_common_options.c       |    5 +
 lib/librte_eal/common/include/rte_eal.h          |   21 +
 lib/librte_eal/linuxapp/eal/Makefile             |    3 +
 lib/librte_eal/linuxapp/eal/eal.c                |  353 +++++--
 lib/librte_eal/linuxapp/eal/rte_eal_version.map  |    4 +
 mk/rte.app.mk                                    |    2 +-
 test/test/test_cfgfile.c                         |   40 +
 test/test/test_cfgfiles/etc/realloc_sections.ini |  128 +++
 25 files changed, 1983 insertions(+), 770 deletions(-)
 create mode 100644 app/test-pmd/config.ini
 create mode 100644 app/test-pmd/config.json
 create mode 100644 test/test/test_cfgfiles/etc/realloc_sections.ini

-- 
2.7.4

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v2 1/7] cfgfile: remove EAL dependency
  2017-06-26 10:59   ` [PATCH v2 0/7] Add support for using a config file for DPDK Jacek Piasecki
@ 2017-06-26 10:59     ` Jacek Piasecki
  2017-06-26 13:12       ` Dumitrescu, Cristian
  2017-06-27 10:26       ` [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Jacek Piasecki
  2017-06-26 10:59     ` [PATCH v2 2/7] cfgfile: add new functions to API Jacek Piasecki
                       ` (5 subsequent siblings)
  6 siblings, 2 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-26 10:59 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Jacek Piasecki

This patch removes the dependency to EAL in cfgfile library.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 lib/librte_cfgfile/Makefile      |  1 +
 lib/librte_cfgfile/rte_cfgfile.c | 29 +++++++++++++++++------------
 2 files changed, 18 insertions(+), 12 deletions(-)

diff --git a/lib/librte_cfgfile/Makefile b/lib/librte_cfgfile/Makefile
index 755ef11..0bee43e 100644
--- a/lib/librte_cfgfile/Makefile
+++ b/lib/librte_cfgfile/Makefile
@@ -38,6 +38,7 @@ LIB = librte_cfgfile.a
 
 CFLAGS += -O3
 CFLAGS += $(WERROR_FLAGS)
+CFLAGS += -I$(SRCDIR)/../librte_eal/common/include
 
 EXPORT_MAP := rte_cfgfile_version.map
 
diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
index b54a523..c6ae3e3 100644
--- a/lib/librte_cfgfile/rte_cfgfile.c
+++ b/lib/librte_cfgfile/rte_cfgfile.c
@@ -36,7 +36,6 @@
 #include <string.h>
 #include <ctype.h>
 #include <rte_common.h>
-#include <rte_string_fns.h>
 
 #include "rte_cfgfile.h"
 
@@ -258,19 +257,25 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 
 			struct rte_cfgfile_section *sect =
 				cfg->sections[curr_section];
-			int n;
+
 			char *split[2] = {NULL};
-			n = rte_strsplit(buffer, sizeof(buffer), split, 2, '=');
-			if (flags & CFG_FLAG_EMPTY_VALUES) {
-				if ((n < 1) || (n > 2)) {
-					printf("Error at line %d - cannot split string, n=%d\n",
-					       lineno, n);
-					goto error1;
-				}
+			split[0] = buffer;
+			split[1] = memchr(buffer, '=', len);
+
+			/* when delimeter not found */
+			if (split[1] == NULL) {
+				printf("Error at line %d - cannot "
+					"split string\n", lineno);
+				goto error1;
 			} else {
-				if (n != 2) {
-					printf("Error at line %d - cannot split string, n=%d\n",
-					       lineno, n);
+				/* when delimeter found */
+				*split[1] = '\0';
+				split[1]++;
+
+				if (!(flags & CFG_FLAG_EMPTY_VALUES) &&
+						(*split[1] == '\0')) {
+					printf("Error at line %d - cannot "
+						"split string\n", lineno);
 					goto error1;
 				}
 			}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v2 2/7] cfgfile: add new functions to API
  2017-06-26 10:59   ` [PATCH v2 0/7] Add support for using a config file for DPDK Jacek Piasecki
  2017-06-26 10:59     ` [PATCH v2 1/7] cfgfile: remove EAL dependency Jacek Piasecki
@ 2017-06-26 10:59     ` Jacek Piasecki
  2017-06-26 10:59     ` [PATCH v2 3/7] cfgfile: rework of load function Jacek Piasecki
                       ` (4 subsequent siblings)
  6 siblings, 0 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-26 10:59 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Jacek Piasecki

Extend existing cfgfile library with providing new API functions:

rte_cfgfile_create() - create new cfgfile object
rte_cfgfile_add_section() - add new section to existing cfgfile
object
rte_cfgfile_add_entry() - add new entry to existing cfgfile
object in specified section
rte_cfgfile_set_entry() - update existing entry in cfgfile object
rte_cfgfile_save() - save existing cfgfile object to INI file

This modification allows to create a cfgfile on
runtime and opens up the possibility to have applications
dynamically build up a proper DPDK configuration, rather than having
to have a pre-existing one.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 lib/librte_cfgfile/rte_cfgfile.c           | 249 +++++++++++++++++++++++++++--
 lib/librte_cfgfile/rte_cfgfile.h           |  76 +++++++++
 lib/librte_cfgfile/rte_cfgfile_version.map |  11 ++
 3 files changed, 323 insertions(+), 13 deletions(-)

diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
index c6ae3e3..518b6ab 100644
--- a/lib/librte_cfgfile/rte_cfgfile.c
+++ b/lib/librte_cfgfile/rte_cfgfile.c
@@ -35,6 +35,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+#include <errno.h>
 #include <rte_common.h>
 
 #include "rte_cfgfile.h"
@@ -42,13 +43,17 @@
 struct rte_cfgfile_section {
 	char name[CFG_NAME_LEN];
 	int num_entries;
-	struct rte_cfgfile_entry *entries[0];
+	int free_entries;
+	int allocated_entries;
+	struct rte_cfgfile_entry **entries;
 };
 
 struct rte_cfgfile {
 	int flags;
 	int num_sections;
-	struct rte_cfgfile_section *sections[0];
+	int free_sections;
+	int allocated_sections;
+	struct rte_cfgfile_section **sections;
 };
 
 /** when we resize a file structure, how many extra entries
@@ -104,6 +109,65 @@ _strip(char *str, unsigned len)
 	return newlen;
 }
 
+static struct rte_cfgfile_section *
+_get_section(struct rte_cfgfile *cfg, const char *sectionname)
+{
+	int i;
+
+	for (i = 0; i < cfg->num_sections; i++) {
+		if (strncmp(cfg->sections[i]->name, sectionname,
+				sizeof(cfg->sections[0]->name)) == 0)
+			return cfg->sections[i];
+	}
+	return NULL;
+}
+
+static int
+_add_entry(struct rte_cfgfile_section *section, const char *entryname,
+		const char *entryvalue)
+{
+	int i;
+
+	/* resize entry structure if we don't have room for more entries */
+	if (section->free_entries == 0) {
+
+		struct rte_cfgfile_entry **n_entries =
+				realloc(section->entries,
+				sizeof(section->entries[0]) *
+				((section->allocated_entries) +
+						CFG_ALLOC_ENTRY_BATCH));
+
+		if (n_entries == NULL)
+			return -ENOMEM;
+
+		section->entries = n_entries;
+
+		for (i = section->allocated_entries;
+				i < (section->allocated_entries) +
+						CFG_ALLOC_ENTRY_BATCH; i++) {
+			section->entries[i] =
+				malloc(sizeof(struct rte_cfgfile_entry));
+
+			if (section->entries[i] == NULL)
+				return -ENOMEM;
+		}
+		section->allocated_entries += CFG_ALLOC_ENTRY_BATCH;
+		section->free_entries += CFG_ALLOC_ENTRY_BATCH;
+	}
+
+	/* fill up entry fields with key name and value */
+	struct rte_cfgfile_entry *curr_entry =
+			section->entries[section->num_entries];
+
+	snprintf(curr_entry->name, sizeof(curr_entry->name), "%s", entryname);
+	snprintf(curr_entry->value, sizeof(curr_entry->value), "%s",
+								entryvalue);
+	section->num_entries++;
+	section->free_entries--;
+
+	return 0;
+}
+
 static int
 rte_cfgfile_check_params(const struct rte_cfgfile_parameters *params)
 {
@@ -332,6 +396,176 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 	return NULL;
 }
 
+struct rte_cfgfile *
+rte_cfgfile_create(int flags)
+{
+	int i, j;
+	struct rte_cfgfile *cfg = NULL;
+
+	cfg = malloc(sizeof(*cfg));
+	if (cfg == NULL)
+		return NULL;
+
+	memset(cfg, 0, sizeof((*cfg)));
+
+	cfg->flags = flags;
+
+	/* allocate first batch of sections and entries */
+	cfg->sections = malloc(sizeof(cfg->sections[0]) *
+						CFG_ALLOC_SECTION_BATCH);
+	if (cfg->sections == NULL)
+		return NULL;
+
+	for (i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) {
+		cfg->sections[i] = malloc(sizeof(struct rte_cfgfile_section));
+		if (cfg->sections[i] == NULL)
+			return NULL;
+
+		memset(cfg->sections[i], 0,
+					sizeof(struct rte_cfgfile_section));
+
+		cfg->sections[i]->entries =
+				malloc(sizeof(cfg->sections[i]->entries[0])
+						* CFG_ALLOC_ENTRY_BATCH);
+		if (cfg->sections[i]->entries == NULL)
+			return NULL;
+
+		for (j = 0; j < CFG_ALLOC_ENTRY_BATCH; j++) {
+			cfg->sections[i]->entries[j] = malloc(sizeof(struct
+							rte_cfgfile_entry));
+			if (cfg->sections[i]->entries[j] == NULL)
+				return NULL;
+		}
+		cfg->sections[i]->allocated_entries = CFG_ALLOC_ENTRY_BATCH;
+		cfg->sections[i]->free_entries = CFG_ALLOC_ENTRY_BATCH;
+	}
+	cfg->allocated_sections = CFG_ALLOC_SECTION_BATCH;
+	cfg->free_sections = CFG_ALLOC_SECTION_BATCH;
+
+	if (flags & CFG_FLAG_GLOBAL_SECTION)
+		rte_cfgfile_add_section(cfg, "GLOBAL");
+	return cfg;
+}
+
+int
+rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname)
+{
+	int i;
+	/* resize overall struct if we don't have room for more	sections */
+	if (cfg->free_sections == 0) {
+
+		struct rte_cfgfile_section **n_sections =
+				realloc(cfg->sections,
+				sizeof(cfg->sections[0]) *
+				((cfg->allocated_sections) +
+						CFG_ALLOC_SECTION_BATCH));
+
+		if (n_sections == NULL)
+			return -ENOMEM;
+
+		cfg->sections = n_sections;
+
+		for (i = cfg->allocated_sections;
+				i < (cfg->allocated_sections) +
+						CFG_ALLOC_SECTION_BATCH; i++) {
+			cfg->sections[i] =
+				malloc(sizeof(struct rte_cfgfile_section));
+
+			if (cfg->sections[i] == NULL)
+				return -ENOMEM;
+
+			memset(cfg->sections[i], 0,
+					sizeof(struct rte_cfgfile_section));
+		}
+		cfg->allocated_sections += CFG_ALLOC_SECTION_BATCH;
+		cfg->free_sections += CFG_ALLOC_SECTION_BATCH;
+	}
+
+	snprintf(cfg->sections[cfg->num_sections]->name,
+			sizeof(cfg->sections[0]->name), "%s", sectionname);
+	cfg->sections[cfg->num_sections]->num_entries = 0;
+
+	cfg->num_sections++;
+	cfg->free_sections--;
+
+	return 0;
+}
+
+int rte_cfgfile_add_entry(struct rte_cfgfile *cfg,
+		const char *sectionname, const char *entryname,
+		const char *entryvalue)
+{
+	int ret;
+
+	if (cfg == NULL)
+		return -EINVAL;
+
+	if (rte_cfgfile_has_entry(cfg, sectionname, entryname) != 0)
+		return -EEXIST;
+
+	/* search for section pointer by sectionname */
+	struct rte_cfgfile_section *curr_section = _get_section(cfg,
+								sectionname);
+	if (curr_section == NULL)
+		return -EINVAL;
+
+	ret = _add_entry(curr_section, entryname, entryvalue);
+
+	return ret;
+}
+
+int rte_cfgfile_set_entry(struct rte_cfgfile *cfg, const char *sectionname,
+		const char *entryname, const char *entryvalue)
+{
+	int i;
+
+	if (cfg == NULL)
+		return -EINVAL;
+
+	/* search for section pointer by sectionname */
+	struct rte_cfgfile_section *curr_section = _get_section(cfg,
+								sectionname);
+	if (curr_section == NULL)
+		return -EINVAL;
+
+	if (entryvalue == NULL)
+		entryvalue = "";
+
+	for (i = 0; i < curr_section->num_entries; i++)
+		if (!strcmp(curr_section->entries[i]->name, entryname)) {
+			strcpy(curr_section->entries[i]->value, entryvalue);
+			return 0;
+		}
+	return -1;
+}
+
+int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename)
+{
+	char buffer[CFG_NAME_LEN + CFG_VALUE_LEN + 4] = {0};
+	int i, j;
+
+	if ((cfg == NULL) || (filename == NULL))
+		return -EINVAL;
+
+	FILE *f = fopen(filename, "w");
+
+	if (f == NULL)
+		return -EINVAL;
+
+	for (i = 0; i < cfg->num_sections; i++) {
+		snprintf(buffer, sizeof(buffer), "[%s]\n",
+					cfg->sections[i]->name);
+		fputs(buffer, f);
+
+		for (j = 0; j < cfg->sections[i]->num_entries; j++) {
+			snprintf(buffer, sizeof(buffer), "%s=%s\n",
+				cfg->sections[i]->entries[j]->name,
+				cfg->sections[i]->entries[j]->value);
+			fputs(buffer, f);
+		}
+	}
+	return fclose(f);
+}
 
 int rte_cfgfile_close(struct rte_cfgfile *cfg)
 {
@@ -385,17 +619,6 @@ rte_cfgfile_sections(struct rte_cfgfile *cfg, char *sections[],
 	return i;
 }
 
-static const struct rte_cfgfile_section *
-_get_section(struct rte_cfgfile *cfg, const char *sectionname)
-{
-	int i;
-	for (i = 0; i < cfg->num_sections; i++) {
-		if (strncmp(cfg->sections[i]->name, sectionname,
-				sizeof(cfg->sections[0]->name)) == 0)
-			return cfg->sections[i];
-	}
-	return NULL;
-}
 
 int
 rte_cfgfile_has_section(struct rte_cfgfile *cfg, const char *sectionname)
diff --git a/lib/librte_cfgfile/rte_cfgfile.h b/lib/librte_cfgfile/rte_cfgfile.h
index fa10d40..6245c7e 100644
--- a/lib/librte_cfgfile/rte_cfgfile.h
+++ b/lib/librte_cfgfile/rte_cfgfile.h
@@ -121,6 +121,82 @@ struct rte_cfgfile *rte_cfgfile_load_with_params(const char *filename,
 	int flags, const struct rte_cfgfile_parameters *params);
 
 /**
+ * Create new cfgfile instance with empty sections and entries
+ *
+ * @param flags
+ *   - CFG_FLAG_GLOBAL_SECTION
+ *     Indicates that the file supports key value entries before the first
+ *     defined section.  These entries can be accessed in the "GLOBAL"
+ *     section.
+ *   - CFG_FLAG_EMPTY_VALUES
+ *     Indicates that file supports key value entries where the value can
+ *     be zero length (e.g., "key=").
+ * @return
+ *   Handle to cfgfile instance on success, NULL otherwise
+ */
+struct rte_cfgfile *rte_cfgfile_create(int flags);
+
+/**
+ * Add section in cfgfile instance.
+ *
+ * @param cfg
+ *   Pointer to the cfgfile structure.
+ * @param sectionname
+ *   Section name which will be add to cfgfile.
+ * @return
+ *   0 on success, -ENOMEM if can't add section
+ */
+int
+rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname);
+
+/**
+ * Add entry to specified section in cfgfile instance.
+ *
+ * @param cfg
+ *   Pointer to the cfgfile structure.
+ * @param sectionname
+ *   Given section name to add an entry.
+ * @param entryname
+ *   Entry name to add.
+ * @entryvalue
+ *   Entry value to add.
+ * @return
+ *   0 on success, -EEXIST if entry already exist, -EINVAL if bad argument
+ */
+int rte_cfgfile_add_entry(struct rte_cfgfile *cfg,
+		const char *sectionname, const char *entryname,
+		const char *entryvalue);
+
+/**
+ * Update value of specified entry name in given section in config file
+ *
+ * @param cfg
+ *   Config file
+ * @param sectionname
+ *   Section name
+ * @param entryname
+ *   Entry name to look for the value change
+ * @param entryvalue
+ *   New entry value. Can be also an empty string if CFG_FLAG_EMPTY_VALUES = 1
+ * @return
+ *   0 on success, -EINVAL if bad argument
+ */
+int rte_cfgfile_set_entry(struct rte_cfgfile *cfg, const char *sectionname,
+		const char *entryname, const char *entryvalue);
+
+/**
+ * Save object cfgfile to file on disc
+ *
+ * @param cfg
+ *   Config file structure
+ * @param filename
+ *   File name to save data
+ * @return
+ *   0 on success, errno otherwise
+ */
+int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename);
+
+/**
 * Get number of sections in config file
 *
 * @param cfg
diff --git a/lib/librte_cfgfile/rte_cfgfile_version.map b/lib/librte_cfgfile/rte_cfgfile_version.map
index 5fe60f7..de68ff6 100644
--- a/lib/librte_cfgfile/rte_cfgfile_version.map
+++ b/lib/librte_cfgfile/rte_cfgfile_version.map
@@ -27,3 +27,14 @@ DPDK_17.05 {
     rte_cfgfile_load_with_params;
 
 } DPDK_16.04;
+
+DPDK_17.08 {
+	global:
+
+	rte_cfgfile_add_entry;
+	rte_cfgfile_add_section;
+	rte_cfgfile_create;
+	rte_cfgfile_save;
+	rte_cfgfile_set_entry;
+
+} DPDK_17.05;
\ No newline at end of file
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v2 3/7] cfgfile: rework of load function
  2017-06-26 10:59   ` [PATCH v2 0/7] Add support for using a config file for DPDK Jacek Piasecki
  2017-06-26 10:59     ` [PATCH v2 1/7] cfgfile: remove EAL dependency Jacek Piasecki
  2017-06-26 10:59     ` [PATCH v2 2/7] cfgfile: add new functions to API Jacek Piasecki
@ 2017-06-26 10:59     ` Jacek Piasecki
  2017-06-26 10:59     ` [PATCH v2 4/7] test/cfgfile: add new unit test Jacek Piasecki
                       ` (3 subsequent siblings)
  6 siblings, 0 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-26 10:59 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Kuba Kozak, Jacek Piasecki

From: Kuba Kozak <kubax.kozak@intel.com>

New functions added to cfgfile library make it possible
to significantly simplify the code of rte_cfgfile_load_with_params()

This patch shows the new body of this function.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 lib/librte_cfgfile/rte_cfgfile.c | 143 +++++----------------------------------
 1 file changed, 17 insertions(+), 126 deletions(-)

diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
index 518b6ab..5625c80 100644
--- a/lib/librte_cfgfile/rte_cfgfile.c
+++ b/lib/librte_cfgfile/rte_cfgfile.c
@@ -207,10 +207,6 @@ struct rte_cfgfile *
 rte_cfgfile_load_with_params(const char *filename, int flags,
 			     const struct rte_cfgfile_parameters *params)
 {
-	int allocated_sections = CFG_ALLOC_SECTION_BATCH;
-	int allocated_entries = 0;
-	int curr_section = -1;
-	int curr_entry = -1;
 	char buffer[CFG_NAME_LEN + CFG_VALUE_LEN + 4] = {0};
 	int lineno = 0;
 	struct rte_cfgfile *cfg = NULL;
@@ -222,28 +218,7 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 	if (f == NULL)
 		return NULL;
 
-	cfg = malloc(sizeof(*cfg) + sizeof(cfg->sections[0]) *
-		allocated_sections);
-	if (cfg == NULL)
-		goto error2;
-
-	memset(cfg->sections, 0, sizeof(cfg->sections[0]) * allocated_sections);
-
-	if (flags & CFG_FLAG_GLOBAL_SECTION) {
-		curr_section = 0;
-		allocated_entries = CFG_ALLOC_ENTRY_BATCH;
-		cfg->sections[curr_section] = malloc(
-			sizeof(*cfg->sections[0]) +
-			sizeof(cfg->sections[0]->entries[0]) *
-			allocated_entries);
-		if (cfg->sections[curr_section] == NULL) {
-			printf("Error - no memory for global section\n");
-			goto error1;
-		}
-
-		snprintf(cfg->sections[curr_section]->name,
-				 sizeof(cfg->sections[0]->name), "GLOBAL");
-	}
+	cfg = rte_cfgfile_create(flags);
 
 	while (fgets(buffer, sizeof(buffer), f) != NULL) {
 		char *pos = NULL;
@@ -254,6 +229,7 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 					"Check if line too long\n", lineno);
 			goto error1;
 		}
+		/* skip parsing if comment character found */
 		pos = memchr(buffer, params->comment_character, len);
 		if (pos != NULL) {
 			*pos = '\0';
@@ -261,6 +237,7 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 		}
 
 		len = _strip(buffer, len);
+		/* skip lines without useful content */
 		if (buffer[0] != '[' && memchr(buffer, '=', len) == NULL)
 			continue;
 
@@ -268,130 +245,44 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 			/* section heading line */
 			char *end = memchr(buffer, ']', len);
 			if (end == NULL) {
-				printf("Error line %d - no terminating '['"
+				printf("Error line %d - no terminating ']'"
 					"character found\n", lineno);
 				goto error1;
 			}
 			*end = '\0';
 			_strip(&buffer[1], end - &buffer[1]);
 
-			/* close off old section and add start new one */
-			if (curr_section >= 0)
-				cfg->sections[curr_section]->num_entries =
-					curr_entry + 1;
-			curr_section++;
-
-			/* resize overall struct if we don't have room for more
-			sections */
-			if (curr_section == allocated_sections) {
-				allocated_sections += CFG_ALLOC_SECTION_BATCH;
-				struct rte_cfgfile *n_cfg = realloc(cfg,
-					sizeof(*cfg) + sizeof(cfg->sections[0])
-					* allocated_sections);
-				if (n_cfg == NULL) {
-					curr_section--;
-					printf("Error - no more memory\n");
-					goto error1;
-				}
-				cfg = n_cfg;
-			}
-
-			/* allocate space for new section */
-			allocated_entries = CFG_ALLOC_ENTRY_BATCH;
-			curr_entry = -1;
-			cfg->sections[curr_section] = malloc(
-				sizeof(*cfg->sections[0]) +
-				sizeof(cfg->sections[0]->entries[0]) *
-				allocated_entries);
-			if (cfg->sections[curr_section] == NULL) {
-				printf("Error - no more memory\n");
-				goto error1;
-			}
-
-			snprintf(cfg->sections[curr_section]->name,
-					sizeof(cfg->sections[0]->name),
-					"%s", &buffer[1]);
+			rte_cfgfile_add_section(cfg, &buffer[1]);
 		} else {
-			/* value line */
-			if (curr_section < 0) {
-				printf("Error line %d - value outside of"
-					"section\n", lineno);
-				goto error1;
-			}
-
-			struct rte_cfgfile_section *sect =
-				cfg->sections[curr_section];
-
+			/* key and value line */
 			char *split[2] = {NULL};
+
 			split[0] = buffer;
 			split[1] = memchr(buffer, '=', len);
+			*split[1] = '\0';
+			split[1]++;
+
+			_strip(split[0], strlen(split[0]));
+			_strip(split[1], strlen(split[1]));
 
-			/* when delimeter not found */
-			if (split[1] == NULL) {
+			if (!(flags & CFG_FLAG_EMPTY_VALUES) &&
+					(*split[1] == '\0')) {
 				printf("Error at line %d - cannot "
 					"split string\n", lineno);
 				goto error1;
-			} else {
-				/* when delimeter found */
-				*split[1] = '\0';
-				split[1]++;
-
-				if (!(flags & CFG_FLAG_EMPTY_VALUES) &&
-						(*split[1] == '\0')) {
-					printf("Error at line %d - cannot "
-						"split string\n", lineno);
-					goto error1;
-				}
 			}
 
-			curr_entry++;
-			if (curr_entry == allocated_entries) {
-				allocated_entries += CFG_ALLOC_ENTRY_BATCH;
-				struct rte_cfgfile_section *n_sect = realloc(
-					sect, sizeof(*sect) +
-					sizeof(sect->entries[0]) *
-					allocated_entries);
-				if (n_sect == NULL) {
-					curr_entry--;
-					printf("Error - no more memory\n");
-					goto error1;
-				}
-				sect = cfg->sections[curr_section] = n_sect;
-			}
-
-			sect->entries[curr_entry] = malloc(
-				sizeof(*sect->entries[0]));
-			if (sect->entries[curr_entry] == NULL) {
-				printf("Error - no more memory\n");
+			if (cfg->num_sections == 0)
 				goto error1;
-			}
 
-			struct rte_cfgfile_entry *entry = sect->entries[
-				curr_entry];
-			snprintf(entry->name, sizeof(entry->name), "%s",
-				split[0]);
-			snprintf(entry->value, sizeof(entry->value), "%s",
-				 split[1] ? split[1] : "");
-			_strip(entry->name, strnlen(entry->name,
-				sizeof(entry->name)));
-			_strip(entry->value, strnlen(entry->value,
-				sizeof(entry->value)));
+			_add_entry(cfg->sections[cfg->num_sections - 1],
+					split[0], (split[1] ? split[1] : ""));
 		}
 	}
 	fclose(f);
-	cfg->flags = flags;
-	cfg->num_sections = curr_section + 1;
-	/* curr_section will still be -1 if we have an empty file */
-	if (curr_section >= 0)
-		cfg->sections[curr_section]->num_entries = curr_entry + 1;
 	return cfg;
-
 error1:
-	cfg->num_sections = curr_section + 1;
-	if (curr_section >= 0)
-		cfg->sections[curr_section]->num_entries = curr_entry + 1;
 	rte_cfgfile_close(cfg);
-error2:
 	fclose(f);
 	return NULL;
 }
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v2 4/7] test/cfgfile: add new unit test
  2017-06-26 10:59   ` [PATCH v2 0/7] Add support for using a config file for DPDK Jacek Piasecki
                       ` (2 preceding siblings ...)
  2017-06-26 10:59     ` [PATCH v2 3/7] cfgfile: rework of load function Jacek Piasecki
@ 2017-06-26 10:59     ` Jacek Piasecki
  2017-06-26 10:59     ` [PATCH v2 5/7] eal: add functions parsing EAL arguments Jacek Piasecki
                       ` (2 subsequent siblings)
  6 siblings, 0 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-26 10:59 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Jacek Piasecki

Load huge realloc_sections.ini file to check malloc/realloc
ability of cfgfile library.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 test/test/test_cfgfile.c                         |  40 +++++++
 test/test/test_cfgfiles/etc/realloc_sections.ini | 128 +++++++++++++++++++++++
 2 files changed, 168 insertions(+)
 create mode 100644 test/test/test_cfgfiles/etc/realloc_sections.ini

diff --git a/test/test/test_cfgfile.c b/test/test/test_cfgfile.c
index 4cc9b14..2278618 100644
--- a/test/test/test_cfgfile.c
+++ b/test/test/test_cfgfile.c
@@ -111,6 +111,7 @@ _test_cfgfile_sample(struct rte_cfgfile *cfgfile)
 	return 0;
 }
 
+
 static int
 test_cfgfile_sample1(void)
 {
@@ -154,6 +155,42 @@ test_cfgfile_sample2(void)
 }
 
 static int
+test_cfgfile_realloc_sections(void)
+{
+	struct rte_cfgfile *cfgfile;
+	int ret;
+	const char *value;
+
+	cfgfile = rte_cfgfile_load(CFG_FILES_ETC "/realloc_sections.ini", 0);
+	TEST_ASSERT_NOT_NULL(cfgfile, "Failed to load config file");
+
+	ret = rte_cfgfile_num_sections(cfgfile, NULL, 0);
+	TEST_ASSERT(ret == 9, "Unexpected number of sections: %d", ret);
+
+	ret = rte_cfgfile_has_section(cfgfile, "section9");
+	TEST_ASSERT(ret, "section9 missing");
+
+	ret = rte_cfgfile_section_num_entries(cfgfile, "section3");
+	TEST_ASSERT(ret == 21,
+			"section3 unexpected number of entries: %d", ret);
+
+	ret = rte_cfgfile_section_num_entries(cfgfile, "section9");
+	TEST_ASSERT(ret == 8, "section9 unexpected number of entries: %d", ret);
+
+	value = rte_cfgfile_get_entry(cfgfile, "section9", "key8");
+	TEST_ASSERT(strcmp("value8_section9", value) == 0,
+		    "key unexpected value: %s", value);
+
+	ret = rte_cfgfile_save(cfgfile, "cfgfile_save.ini");
+	TEST_ASSERT_SUCCESS(ret, "Failed to save *.ini file");
+
+	ret = rte_cfgfile_close(cfgfile);
+	TEST_ASSERT_SUCCESS(ret, "Failed to close cfgfile");
+
+	return 0;
+}
+
+static int
 test_cfgfile_invalid_section_header(void)
 {
 	struct rte_cfgfile *cfgfile;
@@ -292,6 +329,9 @@ test_cfgfile(void)
 	if (test_cfgfile_sample2())
 		return -1;
 
+	if (test_cfgfile_realloc_sections())
+		return -1;
+
 	if (test_cfgfile_invalid_section_header())
 		return -1;
 
diff --git a/test/test/test_cfgfiles/etc/realloc_sections.ini b/test/test/test_cfgfiles/etc/realloc_sections.ini
new file mode 100644
index 0000000..e653e40
--- /dev/null
+++ b/test/test/test_cfgfiles/etc/realloc_sections.ini
@@ -0,0 +1,128 @@
+[section1]
+key1=value1_section1
+key2=value2_section1
+key3=value3_section1
+key4=value4_section1
+key5=value5_section1
+key6=value6_section1
+key7=value7_section1
+key8=value8_section1
+key9=value9_section1
+key10=value10_section1
+key11=value11_section1
+key12=value12_section1
+key13=value13_section1
+key14=value14_section1
+key15=value15_section1
+key16=value16_section1
+key17=value17_section1
+key18=value18_section1
+key19=value19_section1
+key20=value20_section1
+key21=value21_section1
+
+[section2]
+key1=value1_section2
+key2=value2_section2
+key3=value3_section2
+key4=value4_section2
+key5=value5_section2
+key6=value6_section2
+key7=value7_section2
+key8=value8_section2
+key9=value9_section2
+key10=value10_section2
+key11=value11_section2
+key12=value12_section2
+key13=value13_section2
+key14=value14_section2
+key15=value15_section2
+key16=value16_section2
+key17=value17_section2
+key18=value18_section2
+key19=value19_section2
+key20=value20_section2
+key21=value21_section2
+
+[section3]
+key1=value1_section3
+key2=value2_section3
+key3=value3_section3
+key4=value4_section3
+key5=value5_section3
+key6=value6_section3
+key7=value7_section3
+key8=value8_section3
+key9=value9_section3
+key10=value10_section3
+key11=value11_section3
+key12=value12_section3
+key13=value13_section3
+key14=value14_section3
+key15=value15_section3
+key16=value16_section3
+key17=value17_section3
+key18=value18_section3
+key19=value19_section3
+key20=value20_section3
+key21=value21_section3
+
+[section4]
+key1=value1_section4
+key2=value2_section4
+key3=value3_section4
+key4=value4_section4
+key5=value5_section4
+key6=value6_section4
+key7=value7_section4
+key8=value8_section4
+
+[section5]
+key1=value1_section5
+key2=value2_section5
+key3=value3_section5
+key4=value4_section5
+key5=value5_section5
+key6=value6_section5
+key7=value7_section5
+key8=value8_section5
+
+[section6]
+key1=value1_section6
+key2=value2_section6
+key3=value3_section6
+key4=value4_section6
+key5=value5_section6
+key6=value6_section6
+key7=value7_section6
+key8=value8_section6
+
+[section7]
+key1=value1_section7
+key2=value2_section7
+key3=value3_section7
+key4=value4_section7
+key5=value5_section7
+key6=value6_section7
+key7=value7_section7
+key8=value8_section7
+
+[section8]
+key1=value1_section8
+key2=value2_section8
+key3=value3_section8
+key4=value4_section8
+key5=value5_section8
+key6=value6_section8
+key7=value7_section8
+key8=value8_section8
+
+[section9]
+key1=value1_section9
+key2=value2_section9
+key3=value3_section9
+key4=value4_section9
+key5=value5_section9
+key6=value6_section9
+key7=value7_section9
+key8=value8_section9
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v2 5/7] eal: add functions parsing EAL arguments
  2017-06-26 10:59   ` [PATCH v2 0/7] Add support for using a config file for DPDK Jacek Piasecki
                       ` (3 preceding siblings ...)
  2017-06-26 10:59     ` [PATCH v2 4/7] test/cfgfile: add new unit test Jacek Piasecki
@ 2017-06-26 10:59     ` Jacek Piasecki
  2017-06-27 10:52       ` [PATCH v3 0/3] EAL change for using a config file for DPDK Jacek Piasecki
  2017-06-26 10:59     ` [PATCH v2 6/7] app/testpmd: changed example to parse options from cfg file Jacek Piasecki
  2017-06-26 10:59     ` [PATCH v2 7/7] app/testpmd: add parse arguments from JSON config file Jacek Piasecki
  6 siblings, 1 reply; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-26 10:59 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Kuba Kozak

From: Kuba Kozak <kubax.kozak@intel.com>

added function rte_eal_configure which configure
Environment Abstraction Layer (EAL) using
configuration structure.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 config/common_base                              |   1 +
 lib/Makefile                                    |   6 +-
 lib/librte_eal/bsdapp/eal/Makefile              |   4 +
 lib/librte_eal/bsdapp/eal/eal.c                 | 249 ++++++++++++++---
 lib/librte_eal/bsdapp/eal/rte_eal_version.map   |   4 +
 lib/librte_eal/common/eal_common_cpuflags.c     |  14 +-
 lib/librte_eal/common/eal_common_lcore.c        |  11 +-
 lib/librte_eal/common/eal_common_options.c      |   5 +
 lib/librte_eal/common/include/rte_eal.h         |  21 ++
 lib/librte_eal/linuxapp/eal/Makefile            |   3 +
 lib/librte_eal/linuxapp/eal/eal.c               | 353 ++++++++++++++++++------
 lib/librte_eal/linuxapp/eal/rte_eal_version.map |   4 +
 mk/rte.app.mk                                   |   2 +-
 13 files changed, 543 insertions(+), 134 deletions(-)

diff --git a/config/common_base b/config/common_base
index f6aafd1..c1d0e69 100644
--- a/config/common_base
+++ b/config/common_base
@@ -569,6 +569,7 @@ CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
 # Compile librte_cfgfile
 #
 CONFIG_RTE_LIBRTE_CFGFILE=y
+CONFIG_RTE_LIBRTE_CFGFILE_DEBUG=n
 
 #
 # Compile librte_cmdline
diff --git a/lib/Makefile b/lib/Makefile
index 07e1fd0..fc5df3a 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -32,7 +32,11 @@
 include $(RTE_SDK)/mk/rte.vars.mk
 
 DIRS-y += librte_compat
+DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile
 DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+DEPDIRS-librte_eal := librte_cfgfile
+endif
 DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
 DEPDIRS-librte_ring := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
@@ -41,8 +45,6 @@ DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
 DEPDIRS-librte_mbuf := librte_eal librte_mempool
 DIRS-$(CONFIG_RTE_LIBRTE_TIMER) += librte_timer
 DEPDIRS-librte_timer := librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile
-DEPDIRS-librte_cfgfile := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_CMDLINE) += librte_cmdline
 DEPDIRS-librte_cmdline := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_ETHER) += librte_ether
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index a0f9950..d70eefb 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -50,6 +50,10 @@ EXPORT_MAP := rte_eal_version.map
 
 LIBABIVER := 4
 
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -lrte_cfgfile
+endif
+
 # specific to bsdapp exec-env
 SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) := eal.c
 SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += eal_memory.c
diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index 05f0c1f..7baf848 100644
--- a/lib/librte_eal/bsdapp/eal/eal.c
+++ b/lib/librte_eal/bsdapp/eal/eal.c
@@ -73,6 +73,7 @@
 #include <rte_version.h>
 #include <rte_atomic.h>
 #include <malloc_heap.h>
+#include <rte_cfgfile.h>
 
 #include "eal_private.h"
 #include "eal_thread.h"
@@ -309,6 +310,18 @@ eal_get_hugepage_mem_size(void)
 
 /* Parse the arguments for --log-level only */
 static void
+eal_log_level_cfg(struct rte_cfgfile *cfg)
+{
+	const char *entry;
+
+	entry = rte_cfgfile_get_entry(cfg, "DPDK", OPT_LOG_LEVEL);
+	if (entry)
+		eal_parse_common_option(OPT_LOG_LEVEL_NUM, entry,
+				&internal_config);
+}
+
+/* Parse the arguments for --log-level only */
+static void
 eal_log_level_parse(int argc, char **argv)
 {
 	int opt;
@@ -347,6 +360,58 @@ eal_log_level_parse(int argc, char **argv)
 	optarg = old_optarg;
 }
 
+/* Parse single argument */
+static int
+eal_parse_option(int opt, char *optarg, int option_index, char *prgname)
+{
+	int ret;
+
+	/* getopt is not happy, stop right now */
+	if (opt == '?') {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+
+	ret = eal_parse_common_option(opt, optarg, &internal_config);
+	/* common parser is not happy */
+	if (ret < 0) {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+	/* common parser handled this option */
+	if (ret == 0)
+		return 0;
+
+	switch (opt) {
+	case 'h':
+		eal_usage(prgname);
+		exit(EXIT_SUCCESS);
+		break;
+
+	default:
+		if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
+			RTE_LOG(ERR, EAL, "Option %c is not supported "
+				"on FreeBSD\n", opt);
+		} else if (opt >= OPT_LONG_MIN_NUM &&
+			   opt < OPT_LONG_MAX_NUM) {
+			RTE_LOG(ERR, EAL, "Option %s is not supported "
+				"on FreeBSD\n",
+				eal_long_options[option_index].name);
+		} else {
+			RTE_LOG(ERR, EAL, "Option %d is not supported "
+				"on FreeBSD\n", opt);
+		}
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+	return 0;
+out:
+	return ret;
+}
+
 /* Parse the argument given in the command line of the application */
 static int
 eal_parse_args(int argc, char **argv)
@@ -367,45 +432,9 @@ eal_parse_args(int argc, char **argv)
 	while ((opt = getopt_long(argc, argvopt, eal_short_options,
 				  eal_long_options, &option_index)) != EOF) {
 
-		/* getopt is not happy, stop right now */
-		if (opt == '?') {
-			eal_usage(prgname);
-			ret = -1;
-			goto out;
-		}
-
-		ret = eal_parse_common_option(opt, optarg, &internal_config);
-		/* common parser is not happy */
-		if (ret < 0) {
-			eal_usage(prgname);
-			ret = -1;
-			goto out;
-		}
-		/* common parser handled this option */
-		if (ret == 0)
-			continue;
-
-		switch (opt) {
-		case 'h':
-			eal_usage(prgname);
-			exit(EXIT_SUCCESS);
-		default:
-			if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
-				RTE_LOG(ERR, EAL, "Option %c is not supported "
-					"on FreeBSD\n", opt);
-			} else if (opt >= OPT_LONG_MIN_NUM &&
-				   opt < OPT_LONG_MAX_NUM) {
-				RTE_LOG(ERR, EAL, "Option %s is not supported "
-					"on FreeBSD\n",
-					eal_long_options[option_index].name);
-			} else {
-				RTE_LOG(ERR, EAL, "Option %d is not supported "
-					"on FreeBSD\n", opt);
-			}
-			eal_usage(prgname);
-			ret = -1;
+		ret = eal_parse_option(opt, optarg, option_index, prgname);
+		if (ret < 0)
 			goto out;
-		}
 	}
 
 	if (eal_adjust_config(&internal_config) != 0) {
@@ -677,3 +706,147 @@ rte_eal_process_type(void)
 {
 	return rte_config.process_type;
 }
+
+#ifdef RTE_LIBRTE_CFGFILE
+#define vdev_buff_size		200
+#define sectionname_size	20
+static int
+parse_vdev_devices(struct rte_cfgfile *cfg)
+{
+	char sectionname[sectionname_size];
+	char buffer1[vdev_buff_size];
+	int vdev_nb = 0;
+	int n_entries;
+	int i;
+
+	/* ----------- parsing VDEVS */
+	snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
+
+	for (vdev_nb = 1; rte_cfgfile_has_section(cfg, sectionname);
+			vdev_nb++) {
+		n_entries = rte_cfgfile_section_num_entries(cfg, sectionname);
+
+		struct rte_cfgfile_entry entries[n_entries];
+
+		if (n_entries != rte_cfgfile_section_entries(cfg, sectionname,
+				entries, n_entries)) {
+			rte_eal_init_alert("Unexpected fault.");
+			rte_errno = EFAULT;
+			return -1;
+		}
+
+		buffer1[0] = 0;
+		for (i = 0; i < n_entries; i++) {
+			if (strlen(entries[i].value)) {
+
+				if ((strlen(buffer1) +
+						strlen(entries[i].name) +
+						strlen(entries[i].value) + 3)
+						>= vdev_buff_size)
+					goto buff_size_err;
+				strcat(buffer1, entries[i].name);
+				strcat(buffer1, "=");
+				strcat(buffer1, entries[i].value);
+			} else {
+				if ((strlen(buffer1) +
+						strlen(entries[i].name) + 2)
+						>= vdev_buff_size)
+					goto buff_size_err;
+				strcat(buffer1, entries[i].name);
+			}
+
+			if (i < (n_entries - 1))
+				strcat(buffer1, ",");
+		}
+
+		/* parsing vdev */
+		if (rte_eal_devargs_add(RTE_DEVTYPE_VIRTUAL,
+				buffer1) < 0) {
+			return -1;
+		}
+		snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
+	}
+	/* ----------- parsing VDEVS */
+	return 0;
+
+buff_size_err:
+	printf("parse_vdev_devices(): buffer size is to small\n");
+	return -1;
+}
+
+static void
+eal_getopt(const char *str, int *opt, int *option_index)
+{
+	int i;
+
+	*opt = '?';
+	*option_index = 0;
+
+	if (strlen(str) == 1) {
+		*opt = *str;
+		return;
+	}
+
+	for (i = 0; eal_long_options[i].name != NULL; i++) {
+		if (strcmp(str, eal_long_options[i].name) == 0) {
+			*opt = eal_long_options[i].val;
+			*option_index = i;
+			break;
+		}
+	}
+}
+
+int
+rte_eal_configure(struct rte_cfgfile *cfg, char *prgname)
+{
+	int n_entries;
+	int i;
+	int opt;
+	int option_index;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, "DPDK");
+
+	if (n_entries < 1) {
+		printf("No DPDK section entries in cfgfile object\n");
+		return 0;
+	}
+
+	struct rte_cfgfile_entry entries[n_entries];
+
+	if (n_entries !=
+			rte_cfgfile_section_entries(cfg, "DPDK", entries,
+					n_entries)) {
+		rte_eal_init_alert("Unexpected fault.");
+		rte_errno = EFAULT;
+		return -1;
+	}
+
+	eal_reset_internal_config(&internal_config);
+
+	/* set log level as early as possible */
+	eal_log_level_cfg(cfg);
+
+	for (i = 0; i < n_entries; i++) {
+		eal_getopt(entries[i].name, &opt, &option_index);
+
+		if (eal_parse_option(opt, entries[i].value,
+				option_index, prgname) != 0) {
+			rte_eal_init_alert("Invalid config file arguments.");
+			rte_errno = EINVAL;
+			return -1;
+		}
+	}
+
+	if (parse_vdev_devices(cfg) < 0) {
+		rte_eal_init_alert("Couldn't parse vdevs");
+		rte_errno = ENOMEM;
+		return -1;
+	}
+	return 0;
+}
+#endif
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 2e48a73..a939b03 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -193,3 +193,7 @@ DPDK_17.05 {
 	vfio_get_group_no;
 
 } DPDK_17.02;
+
+DPDK_17.08 {
+	rte_eal_configure;
+} DPDK_17.05;
diff --git a/lib/librte_eal/common/eal_common_cpuflags.c b/lib/librte_eal/common/eal_common_cpuflags.c
index 9a2d080..6a365f3 100644
--- a/lib/librte_eal/common/eal_common_cpuflags.c
+++ b/lib/librte_eal/common/eal_common_cpuflags.c
@@ -50,12 +50,18 @@ rte_cpu_check_supported(void)
 int
 rte_cpu_is_supported(void)
 {
+	static int run_once;
+	static int ret;
 	/* This is generated at compile-time by the build system */
 	static const enum rte_cpu_flag_t compile_time_flags[] = {
 			RTE_COMPILE_TIME_CPUFLAGS
 	};
 	unsigned count = RTE_DIM(compile_time_flags), i;
-	int ret;
+
+	/* No need to calculate this function again if we know the result */
+	if (run_once)
+		return ret;
+	run_once = 1;
 
 	for (i = 0; i < count; i++) {
 		ret = rte_cpu_get_flag_enabled(compile_time_flags[i]);
@@ -64,16 +70,16 @@ rte_cpu_is_supported(void)
 			fprintf(stderr,
 				"ERROR: CPU feature flag lookup failed with error %d\n",
 				ret);
-			return 0;
+			return ret = 0;
 		}
 		if (!ret) {
 			fprintf(stderr,
 			        "ERROR: This system does not support \"%s\".\n"
 			        "Please check that RTE_MACHINE is set correctly.\n",
 			        rte_cpu_get_flag_name(compile_time_flags[i]));
-			return 0;
+			return ret = 0;
 		}
 	}
 
-	return 1;
+	return ret = 1;
 }
diff --git a/lib/librte_eal/common/eal_common_lcore.c b/lib/librte_eal/common/eal_common_lcore.c
index 84fa0cb..ce3ef34 100644
--- a/lib/librte_eal/common/eal_common_lcore.c
+++ b/lib/librte_eal/common/eal_common_lcore.c
@@ -53,11 +53,18 @@
 int
 rte_eal_cpu_init(void)
 {
+	static int run_once;
+	static int ret;
 	/* pointer to global configuration */
 	struct rte_config *config = rte_eal_get_configuration();
 	unsigned lcore_id;
 	unsigned count = 0;
 
+	/* No need to calculate this function again if we know the result */
+	if (run_once)
+		return ret;
+	run_once = 1;
+
 	/*
 	 * Parse the maximum set of logical cores, detect the subset of running
 	 * ones and enable them by default.
@@ -91,7 +98,7 @@ rte_eal_cpu_init(void)
 				"RTE_MAX_NUMA_NODES (%d)\n",
 				lcore_config[lcore_id].socket_id,
 				RTE_MAX_NUMA_NODES);
-			return -1;
+			return ret = -1;
 #endif
 		}
 		RTE_LOG(DEBUG, EAL, "Detected lcore %u as "
@@ -107,5 +114,5 @@ rte_eal_cpu_init(void)
 		RTE_MAX_LCORE);
 	RTE_LOG(INFO, EAL, "Detected %u lcore(s)\n", config->lcore_count);
 
-	return 0;
+	return ret = 0;
 }
diff --git a/lib/librte_eal/common/eal_common_options.c b/lib/librte_eal/common/eal_common_options.c
index f470195..138a27f 100644
--- a/lib/librte_eal/common/eal_common_options.c
+++ b/lib/librte_eal/common/eal_common_options.c
@@ -131,8 +131,13 @@ static int core_parsed;
 void
 eal_reset_internal_config(struct internal_config *internal_cfg)
 {
+	static int run_once;
 	int i;
 
+	if (run_once)
+		return;
+	run_once = 1;
+
 	internal_cfg->memory = 0;
 	internal_cfg->force_nrank = 0;
 	internal_cfg->force_nchannel = 0;
diff --git a/lib/librte_eal/common/include/rte_eal.h b/lib/librte_eal/common/include/rte_eal.h
index abf020b..e0705de 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -46,6 +46,8 @@
 #include <rte_per_lcore.h>
 #include <rte_config.h>
 
+struct rte_cfgfile; /* forward declaration of struct */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -188,6 +190,25 @@ int rte_eal_iopl_init(void);
  */
 int rte_eal_init(int argc, char **argv);
 
+#ifdef RTE_LIBRTE_CFGFILE
+/**
+ * Initialize the Environment Abstraction Layer (EAL) using
+ * configuration structure
+ *
+ * @param cfg
+ *   pointer to config file structure.
+ *   If 0 - function free allocated for **cfg_argv memory
+ * @param prgname
+ *   pointer to string with execution path
+ *
+ * @return
+ *  - On success, return 0
+ *  - On failure, returns -1.
+ */
+int
+rte_eal_configure(struct rte_cfgfile *cfg, char *prgname);
+#endif
+
 /**
  * Check if a primary process is currently alive
  *
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 640afd0..656033e 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -50,6 +50,9 @@ LDLIBS += -ldl
 LDLIBS += -lpthread
 LDLIBS += -lgcc_s
 LDLIBS += -lrt
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -lrte_cfgfile
+endif
 
 # specific to linuxapp exec-env
 SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) := eal.c
diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
index 7c78f2d..f5973f4 100644
--- a/lib/librte_eal/linuxapp/eal/eal.c
+++ b/lib/librte_eal/linuxapp/eal/eal.c
@@ -78,6 +78,9 @@
 #include <rte_version.h>
 #include <rte_atomic.h>
 #include <malloc_heap.h>
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#endif
 
 #include "eal_private.h"
 #include "eal_thread.h"
@@ -478,6 +481,20 @@ eal_parse_vfio_intr(const char *mode)
 	return -1;
 }
 
+#ifdef RTE_LIBRTE_CFGFILE
+/* Parse the arguments for --log-level only */
+static void
+eal_log_level_cfg(struct rte_cfgfile *cfg)
+{
+	const char *entry;
+
+	entry = rte_cfgfile_get_entry(cfg, "DPDK", OPT_LOG_LEVEL);
+	if (entry)
+		eal_parse_common_option(OPT_LOG_LEVEL_NUM, entry,
+				&internal_config);
+}
+#endif
+
 /* Parse the arguments for --log-level only */
 static void
 eal_log_level_parse(int argc, char **argv)
@@ -515,119 +532,135 @@ eal_log_level_parse(int argc, char **argv)
 	optarg = old_optarg;
 }
 
-/* Parse the argument given in the command line of the application */
+/* Parse single argument */
 static int
-eal_parse_args(int argc, char **argv)
+eal_parse_option(int opt, char *optarg, int option_index, char *prgname)
 {
-	int opt, ret;
-	char **argvopt;
-	int option_index;
-	char *prgname = argv[0];
-	const int old_optind = optind;
-	const int old_optopt = optopt;
-	char * const old_optarg = optarg;
+	int ret;
 
-	argvopt = argv;
-	optind = 1;
+	/* getopt is not happy, stop right now */
+	if (opt == '?') {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
 
-	while ((opt = getopt_long(argc, argvopt, eal_short_options,
-				  eal_long_options, &option_index)) != EOF) {
+	ret = eal_parse_common_option(opt, optarg, &internal_config);
+	/* common parser is not happy */
+	if (ret < 0) {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+	/* common parser handled this option */
+	if (ret == 0)
+		return 0;
 
-		/* getopt is not happy, stop right now */
-		if (opt == '?') {
+	switch (opt) {
+	case 'h':
+		eal_usage(prgname);
+		exit(EXIT_SUCCESS);
+		break;
+
+	/* long options */
+	case OPT_XEN_DOM0_NUM:
+#ifdef RTE_LIBRTE_XEN_DOM0
+		internal_config.xen_dom0_support = 1;
+		break;
+#else
+		RTE_LOG(ERR, EAL, "Can't support DPDK app "
+			"running on Dom0, please configure"
+			" RTE_LIBRTE_XEN_DOM0=y\n");
+		ret = -1;
+		goto out;
+#endif
+
+	case OPT_HUGE_DIR_NUM:
+		internal_config.hugepage_dir = optarg;
+		break;
+
+	case OPT_FILE_PREFIX_NUM:
+		internal_config.hugefile_prefix = optarg;
+		break;
+
+	case OPT_SOCKET_MEM_NUM:
+		if (eal_parse_socket_mem(optarg) < 0) {
+			RTE_LOG(ERR, EAL, "invalid parameters for --"
+					OPT_SOCKET_MEM "\n");
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
 		}
+		break;
 
-		ret = eal_parse_common_option(opt, optarg, &internal_config);
-		/* common parser is not happy */
-		if (ret < 0) {
+	case OPT_BASE_VIRTADDR_NUM:
+		if (eal_parse_base_virtaddr(optarg) < 0) {
+			RTE_LOG(ERR, EAL, "invalid parameter for --"
+					OPT_BASE_VIRTADDR "\n");
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
 		}
-		/* common parser handled this option */
-		if (ret == 0)
-			continue;
+		break;
 
-		switch (opt) {
-		case 'h':
+	case OPT_VFIO_INTR_NUM:
+		if (eal_parse_vfio_intr(optarg) < 0) {
+			RTE_LOG(ERR, EAL, "invalid parameters for --"
+					OPT_VFIO_INTR "\n");
 			eal_usage(prgname);
-			exit(EXIT_SUCCESS);
-
-		/* long options */
-		case OPT_XEN_DOM0_NUM:
-#ifdef RTE_LIBRTE_XEN_DOM0
-			internal_config.xen_dom0_support = 1;
-#else
-			RTE_LOG(ERR, EAL, "Can't support DPDK app "
-				"running on Dom0, please configure"
-				" RTE_LIBRTE_XEN_DOM0=y\n");
 			ret = -1;
 			goto out;
-#endif
-			break;
+		}
+		break;
 
-		case OPT_HUGE_DIR_NUM:
-			internal_config.hugepage_dir = optarg;
-			break;
+	case OPT_CREATE_UIO_DEV_NUM:
+		internal_config.create_uio_dev = 1;
+		break;
 
-		case OPT_FILE_PREFIX_NUM:
-			internal_config.hugefile_prefix = optarg;
-			break;
+	default:
+		if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
+			RTE_LOG(ERR, EAL, "Option %c is not supported "
+				"on Linux\n", opt);
+		} else if (opt >= OPT_LONG_MIN_NUM &&
+			   opt < OPT_LONG_MAX_NUM) {
+			RTE_LOG(ERR, EAL, "Option %s is not supported "
+				"on Linux\n",
+				eal_long_options[option_index].name);
+		} else {
+			RTE_LOG(ERR, EAL, "Option %d is not supported "
+				"on Linux\n", opt);
+		}
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
 
-		case OPT_SOCKET_MEM_NUM:
-			if (eal_parse_socket_mem(optarg) < 0) {
-				RTE_LOG(ERR, EAL, "invalid parameters for --"
-						OPT_SOCKET_MEM "\n");
-				eal_usage(prgname);
-				ret = -1;
-				goto out;
-			}
-			break;
+	return 0;
+out:
+	return ret;
+}
 
-		case OPT_BASE_VIRTADDR_NUM:
-			if (eal_parse_base_virtaddr(optarg) < 0) {
-				RTE_LOG(ERR, EAL, "invalid parameter for --"
-						OPT_BASE_VIRTADDR "\n");
-				eal_usage(prgname);
-				ret = -1;
-				goto out;
-			}
-			break;
+/* Parse the argument given in the command line of the application */
+static int
+eal_parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	const int old_optind = optind;
+	const int old_optopt = optopt;
+	char * const old_optarg = optarg;
 
-		case OPT_VFIO_INTR_NUM:
-			if (eal_parse_vfio_intr(optarg) < 0) {
-				RTE_LOG(ERR, EAL, "invalid parameters for --"
-						OPT_VFIO_INTR "\n");
-				eal_usage(prgname);
-				ret = -1;
-				goto out;
-			}
-			break;
+	argvopt = argv;
+	optind = 1;
 
-		case OPT_CREATE_UIO_DEV_NUM:
-			internal_config.create_uio_dev = 1;
-			break;
+	while ((opt = getopt_long(argc, argvopt, eal_short_options,
+				  eal_long_options, &option_index)) != EOF) {
 
-		default:
-			if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
-				RTE_LOG(ERR, EAL, "Option %c is not supported "
-					"on Linux\n", opt);
-			} else if (opt >= OPT_LONG_MIN_NUM &&
-				   opt < OPT_LONG_MAX_NUM) {
-				RTE_LOG(ERR, EAL, "Option %s is not supported "
-					"on Linux\n",
-					eal_long_options[option_index].name);
-			} else {
-				RTE_LOG(ERR, EAL, "Option %d is not supported "
-					"on Linux\n", opt);
-			}
-			eal_usage(prgname);
-			ret = -1;
+		ret = eal_parse_option(opt, optarg, option_index, prgname);
+		if (ret < 0)
 			goto out;
-		}
 	}
 
 	if (eal_adjust_config(&internal_config) != 0) {
@@ -995,3 +1028,149 @@ rte_eal_check_module(const char *module_name)
 	/* Module has been found */
 	return 1;
 }
+
+#ifdef RTE_LIBRTE_CFGFILE
+#define vdev_buff_size		200
+#define sectionname_size	20
+static int
+parse_vdev_devices(struct rte_cfgfile *cfg)
+{
+	char sectionname[sectionname_size];
+	char buffer1[vdev_buff_size];
+	int vdev_nb = 0;
+	int n_entries;
+
+	int i;
+
+	/* ----------- parsing VDEVS */
+	snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
+
+	for (vdev_nb = 1; rte_cfgfile_has_section(cfg, sectionname);
+			vdev_nb++) {
+		n_entries = rte_cfgfile_section_num_entries(cfg, sectionname);
+
+		struct rte_cfgfile_entry entries[n_entries];
+
+
+		if (n_entries != rte_cfgfile_section_entries(cfg, sectionname,
+				entries, n_entries)) {
+			rte_eal_init_alert("Unexpected fault.");
+			rte_errno = EFAULT;
+			return -1;
+		}
+
+		buffer1[0] = 0;
+		for (i = 0; i < n_entries; i++) {
+			if (strlen(entries[i].value)) {
+
+				if ((strlen(buffer1) +
+						strlen(entries[i].name) +
+						strlen(entries[i].value) + 3)
+						>= vdev_buff_size)
+					goto buff_size_err;
+				strcat(buffer1, entries[i].name);
+				strcat(buffer1, "=");
+				strcat(buffer1, entries[i].value);
+			} else {
+				if ((strlen(buffer1) +
+						strlen(entries[i].name) + 2)
+						>= vdev_buff_size)
+					goto buff_size_err;
+				strcat(buffer1, entries[i].name);
+			}
+
+			if (i < (n_entries - 1))
+				strcat(buffer1, ",");
+		}
+
+		/* parsing vdev */
+		if (rte_eal_devargs_add(RTE_DEVTYPE_VIRTUAL,
+				buffer1) < 0) {
+			return -1;
+		}
+		snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
+	}
+	/* ----------- parsing VDEVS */
+	return 0;
+
+buff_size_err:
+	printf("parse_vdev_devices(): buffer size is to small\n");
+	return -1;
+}
+
+static void
+eal_getopt(const char *str, int *opt, int *option_index)
+{
+	int i;
+
+	*opt = '?';
+	*option_index = 0;
+
+	if (strlen(str) == 1) {
+		*opt = *str;
+		return;
+	}
+
+	for (i = 0; eal_long_options[i].name != NULL; i++) {
+		if (strcmp(str, eal_long_options[i].name) == 0) {
+			*opt = eal_long_options[i].val;
+			*option_index = i;
+			break;
+		}
+	}
+}
+
+int
+rte_eal_configure(struct rte_cfgfile *cfg, char *prgname)
+{
+	int n_entries;
+	int i;
+	int opt;
+	int option_index;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, "DPDK");
+
+	if (n_entries < 1) {
+		printf("No DPDK section entries in cfgfile object\n");
+		return 0;
+	}
+
+	struct rte_cfgfile_entry entries[n_entries];
+
+	if (n_entries !=
+			rte_cfgfile_section_entries(cfg, "DPDK", entries,
+					n_entries)) {
+		rte_eal_init_alert("Unexpected fault.");
+		rte_errno = EFAULT;
+		return -1;
+	}
+
+	eal_reset_internal_config(&internal_config);
+
+	/* set log level as early as possible */
+	eal_log_level_cfg(cfg);
+
+	for (i = 0; i < n_entries; i++) {
+		eal_getopt(entries[i].name, &opt, &option_index);
+
+		if (eal_parse_option(opt, entries[i].value,
+				option_index, prgname) != 0) {
+			rte_eal_init_alert("Invalid config file arguments.");
+			rte_errno = EINVAL;
+			return -1;
+		}
+	}
+
+	if (parse_vdev_devices(cfg) < 0) {
+		rte_eal_init_alert("Couldn't parse vdevs");
+		rte_errno = ENOMEM;
+		return -1;
+	}
+	return 0;
+}
+#endif
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 670bab3..c93e6d9 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -198,3 +198,7 @@ DPDK_17.05 {
 	vfio_get_group_no;
 
 } DPDK_17.02;
+
+DPDK_17.08 {
+	rte_eal_configure;
+} DPDK_17.05;
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index bcaf1b3..642af92 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,7 +80,6 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_POWER)          += -lrte_power
 
 _LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER)          += -lrte_timer
 _LDLIBS-$(CONFIG_RTE_LIBRTE_EFD)            += -lrte_efd
-_LDLIBS-$(CONFIG_RTE_LIBRTE_CFGFILE)        += -lrte_cfgfile
 
 _LDLIBS-y += --whole-archive
 
@@ -96,6 +95,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL)        += -lrte_mempool
 _LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING)   += -lrte_mempool_ring
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RING)           += -lrte_ring
 _LDLIBS-$(CONFIG_RTE_LIBRTE_EAL)            += -lrte_eal
+_LDLIBS-$(CONFIG_RTE_LIBRTE_CFGFILE)        += -lrte_cfgfile
 _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline
 _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v2 6/7] app/testpmd: changed example to parse options from cfg file
  2017-06-26 10:59   ` [PATCH v2 0/7] Add support for using a config file for DPDK Jacek Piasecki
                       ` (4 preceding siblings ...)
  2017-06-26 10:59     ` [PATCH v2 5/7] eal: add functions parsing EAL arguments Jacek Piasecki
@ 2017-06-26 10:59     ` Jacek Piasecki
  2017-06-26 10:59     ` [PATCH v2 7/7] app/testpmd: add parse arguments from JSON config file Jacek Piasecki
  6 siblings, 0 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-26 10:59 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Kuba Kozak

From: Kuba Kozak <kubax.kozak@intel.com>

This patch shows how to pass arguments to application
using config.ini file.

If a --cfgfile-path <path> option is passed into commandline
non EAL section, then the file is loaded and used by app.

If a config.ini file is present in current working
directory, and no --cfgfile-path option is passed in, config.ini
file will be loaded and used by app as default configuration.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test-pmd/config.ini   |   24 +
 app/test-pmd/parameters.c | 1193 ++++++++++++++++++++++++++-------------------
 app/test-pmd/testpmd.c    |   67 ++-
 app/test-pmd/testpmd.h    |    3 +-
 4 files changed, 790 insertions(+), 497 deletions(-)
 create mode 100644 app/test-pmd/config.ini

diff --git a/app/test-pmd/config.ini b/app/test-pmd/config.ini
new file mode 100644
index 0000000..54c83a2
--- /dev/null
+++ b/app/test-pmd/config.ini
@@ -0,0 +1,24 @@
+[DPDK]
+v =
+l = 0-4
+n = 4
+master-lcore = 0
+proc-type = primary
+
+[TEST-PMD]
+i =
+portmask = 0xff
+nb-cores = 4
+port-topology = paired
+
+[DPDK.vdev0]
+net_ring0 =
+
+[DPDK.vdev1]
+net_ring1 =
+
+[DPDK.vdev2]
+net_ring2 =
+
+[DPDK.vdev3]
+net_ring3 =
\ No newline at end of file
diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index fbe6284..4202691 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -77,9 +77,96 @@
 #include <rte_eth_bond.h>
 #endif
 #include <rte_flow.h>
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#endif
 
 #include "testpmd.h"
 
+enum { TX, RX };
+
+static struct option lgopts[] = {
+	{ "cfgfile-path",		1, 0, 1 },
+	{ "help",			0, 0, 0 },
+#ifdef RTE_LIBRTE_CMDLINE
+	{ "interactive",		0, 0, 0 },
+	{ "cmdline-file",		1, 0, 0 },
+	{ "auto-start",			0, 0, 0 },
+	{ "eth-peers-configfile",	1, 0, 0 },
+	{ "eth-peer",			1, 0, 0 },
+#endif
+	{ "ports",			1, 0, 0 },
+	{ "nb-cores",			1, 0, 0 },
+	{ "nb-ports",			1, 0, 0 },
+	{ "coremask",			1, 0, 0 },
+	{ "portmask",			1, 0, 0 },
+	{ "numa",			0, 0, 0 },
+	{ "no-numa",			0, 0, 0 },
+	{ "mp-anon",			0, 0, 0 },
+	{ "port-numa-config",           1, 0, 0 },
+	{ "ring-numa-config",           1, 0, 0 },
+	{ "socket-num",			1, 0, 0 },
+	{ "mbuf-size",			1, 0, 0 },
+	{ "total-num-mbufs",		1, 0, 0 },
+	{ "max-pkt-len",		1, 0, 0 },
+	{ "pkt-filter-mode",            1, 0, 0 },
+	{ "pkt-filter-report-hash",     1, 0, 0 },
+	{ "pkt-filter-size",            1, 0, 0 },
+	{ "pkt-filter-drop-queue",      1, 0, 0 },
+#ifdef RTE_LIBRTE_LATENCY_STATS
+	{ "latencystats",               1, 0, 0 },
+#endif
+#ifdef RTE_LIBRTE_BITRATE
+	{ "bitrate-stats",              1, 0, 0 },
+#endif
+	{ "disable-crc-strip",          0, 0, 0 },
+	{ "enable-lro",                 0, 0, 0 },
+	{ "enable-rx-cksum",            0, 0, 0 },
+	{ "enable-scatter",             0, 0, 0 },
+	{ "disable-hw-vlan",            0, 0, 0 },
+	{ "disable-hw-vlan-filter",     0, 0, 0 },
+	{ "disable-hw-vlan-strip",      0, 0, 0 },
+	{ "disable-hw-vlan-extend",     0, 0, 0 },
+	{ "enable-drop-en",            0, 0, 0 },
+	{ "disable-rss",                0, 0, 0 },
+	{ "port-topology",              1, 0, 0 },
+	{ "forward-mode",               1, 0, 0 },
+	{ "rss-ip",			0, 0, 0 },
+	{ "rss-udp",			0, 0, 0 },
+	{ "rxq",			1, 0, 0 },
+	{ "txq",			1, 0, 0 },
+	{ "rxd",			1, 0, 0 },
+	{ "txd",			1, 0, 0 },
+	{ "burst",			1, 0, 0 },
+	{ "mbcache",			1, 0, 0 },
+	{ "txpt",			1, 0, 0 },
+	{ "txht",			1, 0, 0 },
+	{ "txwt",			1, 0, 0 },
+	{ "txfreet",			1, 0, 0 },
+	{ "txrst",			1, 0, 0 },
+	{ "txqflags",			1, 0, 0 },
+	{ "rxpt",			1, 0, 0 },
+	{ "rxht",			1, 0, 0 },
+	{ "rxwt",			1, 0, 0 },
+	{ "rxfreet",                    1, 0, 0 },
+	{ "tx-queue-stats-mapping",	1, 0, 0 },
+	{ "rx-queue-stats-mapping",	1, 0, 0 },
+	{ "no-flush-rx",	0, 0, 0 },
+	{ "txpkts",			1, 0, 0 },
+	{ "disable-link-check",		0, 0, 0 },
+	{ "no-lsc-interrupt",		0, 0, 0 },
+	{ "no-rmv-interrupt",		0, 0, 0 },
+	{ "print-event",		1, 0, 0 },
+	{ "mask-event",			1, 0, 0 },
+	{ 0, 0, 0, 0 },
+};
+
+#ifdef RTE_LIBRTE_CMDLINE
+#define SHORTOPTS "i"
+#else
+#define SHORTOPTS ""
+#endif
+
 static void
 usage(char* progname)
 {
@@ -549,559 +636,675 @@ parse_event_printing_config(const char *optarg, int enable)
 	return 0;
 }
 
-void
-launch_args_parse(int argc, char** argv)
+static int
+parse_option(int opt, char *optarg, int opt_idx, char *prgname)
 {
-	int n, opt;
-	char **argvopt;
-	int opt_idx;
-	enum { TX, RX };
+	int n;
 
-	static struct option lgopts[] = {
-		{ "help",			0, 0, 0 },
+	switch (opt) {
 #ifdef RTE_LIBRTE_CMDLINE
-		{ "interactive",		0, 0, 0 },
-		{ "cmdline-file",		1, 0, 0 },
-		{ "auto-start",			0, 0, 0 },
-		{ "eth-peers-configfile",	1, 0, 0 },
-		{ "eth-peer",			1, 0, 0 },
-#endif
-		{ "ports",			1, 0, 0 },
-		{ "nb-cores",			1, 0, 0 },
-		{ "nb-ports",			1, 0, 0 },
-		{ "coremask",			1, 0, 0 },
-		{ "portmask",			1, 0, 0 },
-		{ "numa",			0, 0, 0 },
-		{ "no-numa",			0, 0, 0 },
-		{ "mp-anon",			0, 0, 0 },
-		{ "port-numa-config",           1, 0, 0 },
-		{ "ring-numa-config",           1, 0, 0 },
-		{ "socket-num",			1, 0, 0 },
-		{ "mbuf-size",			1, 0, 0 },
-		{ "total-num-mbufs",		1, 0, 0 },
-		{ "max-pkt-len",		1, 0, 0 },
-		{ "pkt-filter-mode",            1, 0, 0 },
-		{ "pkt-filter-report-hash",     1, 0, 0 },
-		{ "pkt-filter-size",            1, 0, 0 },
-		{ "pkt-filter-drop-queue",      1, 0, 0 },
-#ifdef RTE_LIBRTE_LATENCY_STATS
-		{ "latencystats",               1, 0, 0 },
-#endif
-#ifdef RTE_LIBRTE_BITRATE
-		{ "bitrate-stats",              1, 0, 0 },
+	case 'i':
+		printf("Interactive-mode selected\n");
+		interactive = 1;
+		break;
 #endif
-		{ "disable-crc-strip",          0, 0, 0 },
-		{ "enable-lro",                 0, 0, 0 },
-		{ "enable-rx-cksum",            0, 0, 0 },
-		{ "enable-scatter",             0, 0, 0 },
-		{ "disable-hw-vlan",            0, 0, 0 },
-		{ "disable-hw-vlan-filter",     0, 0, 0 },
-		{ "disable-hw-vlan-strip",      0, 0, 0 },
-		{ "disable-hw-vlan-extend",     0, 0, 0 },
-		{ "enable-drop-en",            0, 0, 0 },
-		{ "disable-rss",                0, 0, 0 },
-		{ "port-topology",              1, 0, 0 },
-		{ "forward-mode",               1, 0, 0 },
-		{ "rss-ip",			0, 0, 0 },
-		{ "rss-udp",			0, 0, 0 },
-		{ "rxq",			1, 0, 0 },
-		{ "txq",			1, 0, 0 },
-		{ "rxd",			1, 0, 0 },
-		{ "txd",			1, 0, 0 },
-		{ "burst",			1, 0, 0 },
-		{ "mbcache",			1, 0, 0 },
-		{ "txpt",			1, 0, 0 },
-		{ "txht",			1, 0, 0 },
-		{ "txwt",			1, 0, 0 },
-		{ "txfreet",			1, 0, 0 },
-		{ "txrst",			1, 0, 0 },
-		{ "txqflags",			1, 0, 0 },
-		{ "rxpt",			1, 0, 0 },
-		{ "rxht",			1, 0, 0 },
-		{ "rxwt",			1, 0, 0 },
-		{ "rxfreet",                    1, 0, 0 },
-		{ "tx-queue-stats-mapping",	1, 0, 0 },
-		{ "rx-queue-stats-mapping",	1, 0, 0 },
-		{ "no-flush-rx",	0, 0, 0 },
-		{ "txpkts",			1, 0, 0 },
-		{ "disable-link-check",		0, 0, 0 },
-		{ "no-lsc-interrupt",		0, 0, 0 },
-		{ "no-rmv-interrupt",		0, 0, 0 },
-		{ "print-event",		1, 0, 0 },
-		{ "mask-event",			1, 0, 0 },
-		{ 0, 0, 0, 0 },
-	};
+	case 'a':
+		printf("Auto-start selected\n");
+		auto_start = 1;
+		break;
 
-	argvopt = argv;
-
-#ifdef RTE_LIBRTE_CMDLINE
-#define SHORTOPTS "i"
-#else
-#define SHORTOPTS ""
-#endif
-	while ((opt = getopt_long(argc, argvopt, SHORTOPTS "ah",
-				 lgopts, &opt_idx)) != EOF) {
-		switch (opt) {
+	case 0: /*long options */
+		if (!strcmp(lgopts[opt_idx].name, "help")) {
+			usage(prgname);
+			rte_exit(EXIT_SUCCESS, "Displayed help\n");
+			return 0;
+		}
 #ifdef RTE_LIBRTE_CMDLINE
-		case 'i':
+		if (!strcmp(lgopts[opt_idx].name, "interactive")) {
 			printf("Interactive-mode selected\n");
 			interactive = 1;
-			break;
-#endif
-		case 'a':
+		}
+		if (!strcmp(lgopts[opt_idx].name, "cmdline-file")) {
+			printf("CLI commands to be read from %s\n",
+			       optarg);
+			snprintf(cmdline_filename,
+				 sizeof(cmdline_filename), "%s",
+				 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "auto-start")) {
 			printf("Auto-start selected\n");
 			auto_start = 1;
-			break;
+		}
+		if (!strcmp(lgopts[opt_idx].name,
+			    "eth-peers-configfile")) {
+			if (init_peer_eth_addrs(optarg) != 0)
+				rte_exit(EXIT_FAILURE,
+					 "Cannot open logfile\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "eth-peer")) {
+			char *port_end;
+			uint8_t c, peer_addr[6];
 
-		case 0: /*long options */
-			if (!strcmp(lgopts[opt_idx].name, "help")) {
-				usage(argv[0]);
-				rte_exit(EXIT_SUCCESS, "Displayed help\n");
-			}
-#ifdef RTE_LIBRTE_CMDLINE
-			if (!strcmp(lgopts[opt_idx].name, "interactive")) {
-				printf("Interactive-mode selected\n");
-				interactive = 1;
-			}
-			if (!strcmp(lgopts[opt_idx].name, "cmdline-file")) {
-				printf("CLI commands to be read from %s\n",
-				       optarg);
-				snprintf(cmdline_filename,
-					 sizeof(cmdline_filename), "%s",
-					 optarg);
-			}
-			if (!strcmp(lgopts[opt_idx].name, "auto-start")) {
-				printf("Auto-start selected\n");
-				auto_start = 1;
-			}
-			if (!strcmp(lgopts[opt_idx].name,
-				    "eth-peers-configfile")) {
-				if (init_peer_eth_addrs(optarg) != 0)
-					rte_exit(EXIT_FAILURE,
-						 "Cannot open logfile\n");
+			errno = 0;
+			n = strtoul(optarg, &port_end, 10);
+			if (errno != 0 || port_end == optarg ||
+					*port_end++ != ',')
+				rte_exit(EXIT_FAILURE,
+					 "Invalid eth-peer: %s", optarg);
+			if (n >= RTE_MAX_ETHPORTS) {
+				rte_exit(EXIT_FAILURE,
+					 "eth-peer: port %d >= "
+					 "RTE_MAX_ETHPORTS(%d)\n",
+					 n, RTE_MAX_ETHPORTS);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "eth-peer")) {
-				char *port_end;
-				uint8_t c, peer_addr[6];
-
-				errno = 0;
-				n = strtoul(optarg, &port_end, 10);
-				if (errno != 0 || port_end == optarg || *port_end++ != ',')
-					rte_exit(EXIT_FAILURE,
-						 "Invalid eth-peer: %s", optarg);
-				if (n >= RTE_MAX_ETHPORTS)
-					rte_exit(EXIT_FAILURE,
-						 "eth-peer: port %d >= RTE_MAX_ETHPORTS(%d)\n",
-						 n, RTE_MAX_ETHPORTS);
 
-				if (cmdline_parse_etheraddr(NULL, port_end,
-						&peer_addr, sizeof(peer_addr)) < 0)
-					rte_exit(EXIT_FAILURE,
-						 "Invalid ethernet address: %s\n",
-						 port_end);
-				for (c = 0; c < 6; c++)
-					peer_eth_addrs[n].addr_bytes[c] =
-						peer_addr[c];
-				nb_peer_eth_addrs++;
+			if (cmdline_parse_etheraddr(NULL, port_end,
+					&peer_addr, sizeof(peer_addr)) < 0) {
+				rte_exit(EXIT_FAILURE,
+					 "Invalid ethernet address: %s\n",
+					 port_end);
+				return -1;
 			}
+			for (c = 0; c < 6; c++)
+				peer_eth_addrs[n].addr_bytes[c] =
+					peer_addr[c];
+			nb_peer_eth_addrs++;
+		}
 #endif
-			if (!strcmp(lgopts[opt_idx].name, "nb-ports")) {
-				n = atoi(optarg);
-				if (n > 0 && n <= nb_ports)
-					nb_fwd_ports = (uint8_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "Invalid port %d\n", n);
+		if (!strcmp(lgopts[opt_idx].name, "nb-ports")) {
+			n = atoi(optarg);
+			if (n > 0 && n <= nb_ports)
+				nb_fwd_ports = (uint8_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "Invalid port %d\n", n);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "nb-cores")) {
-				n = atoi(optarg);
-				if (n > 0 && n <= nb_lcores)
-					nb_fwd_lcores = (uint8_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "nb-cores should be > 0 and <= %d\n",
-						 nb_lcores);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "nb-cores")) {
+			n = atoi(optarg);
+			if (n > 0 && n <= nb_lcores)
+				nb_fwd_lcores = (uint8_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "nb-cores should be > 0 and <= %d\n",
+					 nb_lcores);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "coremask"))
-				parse_fwd_coremask(optarg);
-			if (!strcmp(lgopts[opt_idx].name, "portmask"))
-				parse_fwd_portmask(optarg);
-			if (!strcmp(lgopts[opt_idx].name, "no-numa"))
-				numa_support = 0;
-			if (!strcmp(lgopts[opt_idx].name, "numa"))
-				numa_support = 1;
-			if (!strcmp(lgopts[opt_idx].name, "mp-anon")) {
-				mp_anon = 1;
+		}
+		if (!strcmp(lgopts[opt_idx].name, "coremask"))
+			parse_fwd_coremask(optarg);
+		if (!strcmp(lgopts[opt_idx].name, "portmask"))
+			parse_fwd_portmask(optarg);
+		if (!strcmp(lgopts[opt_idx].name, "no-numa"))
+			numa_support = 0;
+		if (!strcmp(lgopts[opt_idx].name, "numa"))
+			numa_support = 1;
+		if (!strcmp(lgopts[opt_idx].name, "mp-anon"))
+			mp_anon = 1;
+		if (!strcmp(lgopts[opt_idx].name, "port-numa-config")) {
+			if (parse_portnuma_config(optarg)) {
+				rte_exit(EXIT_FAILURE,
+				   "invalid port-numa configuration\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "port-numa-config")) {
-				if (parse_portnuma_config(optarg))
-					rte_exit(EXIT_FAILURE,
-					   "invalid port-numa configuration\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "ring-numa-config"))
+			if (parse_ringnuma_config(optarg)) {
+				rte_exit(EXIT_FAILURE,
+				   "invalid ring-numa configuration\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "ring-numa-config"))
-				if (parse_ringnuma_config(optarg))
-					rte_exit(EXIT_FAILURE,
-					   "invalid ring-numa configuration\n");
-			if (!strcmp(lgopts[opt_idx].name, "socket-num")) {
-				n = atoi(optarg);
-				if (!new_socket_id((uint8_t)n)) {
-					socket_num = (uint8_t)n;
-				} else {
-					print_invalid_socket_id_error();
-					rte_exit(EXIT_FAILURE,
-						"Invalid socket id");
-				}
+		if (!strcmp(lgopts[opt_idx].name, "socket-num")) {
+			n = atoi(optarg);
+			if (!new_socket_id((uint8_t)n)) {
+				socket_num = (uint8_t)n;
+			} else {
+				print_invalid_socket_id_error();
+				rte_exit(EXIT_FAILURE,
+					"Invalid socket id");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "mbuf-size")) {
-				n = atoi(optarg);
-				if (n > 0 && n <= 0xFFFF)
-					mbuf_data_size = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "mbuf-size should be > 0 and < 65536\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "mbuf-size")) {
+			n = atoi(optarg);
+			if (n > 0 && n <= 0xFFFF)
+				mbuf_data_size = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "mbuf-size should be > 0 and < 65536\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "total-num-mbufs")) {
-				n = atoi(optarg);
-				if (n > 1024)
-					param_total_num_mbufs = (unsigned)n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "total-num-mbufs should be > 1024\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "total-num-mbufs")) {
+			n = atoi(optarg);
+			if (n > 1024)
+				param_total_num_mbufs = (unsigned int)n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "total-num-mbufs should be > 1024\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "max-pkt-len")) {
-				n = atoi(optarg);
-				if (n >= ETHER_MIN_LEN) {
-					rx_mode.max_rx_pkt_len = (uint32_t) n;
-					if (n > ETHER_MAX_LEN)
-					    rx_mode.jumbo_frame = 1;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "Invalid max-pkt-len=%d - should be > %d\n",
-						 n, ETHER_MIN_LEN);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "max-pkt-len")) {
+			n = atoi(optarg);
+			if (n >= ETHER_MIN_LEN) {
+				rx_mode.max_rx_pkt_len = (uint32_t) n;
+				if (n > ETHER_MAX_LEN)
+					rx_mode.jumbo_frame = 1;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "Invalid max-pkt-len=%d - should be > %d\n",
+					 n, ETHER_MIN_LEN);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "pkt-filter-mode")) {
-				if (!strcmp(optarg, "signature"))
-					fdir_conf.mode =
-						RTE_FDIR_MODE_SIGNATURE;
-				else if (!strcmp(optarg, "perfect"))
-					fdir_conf.mode = RTE_FDIR_MODE_PERFECT;
-				else if (!strcmp(optarg, "perfect-mac-vlan"))
-					fdir_conf.mode = RTE_FDIR_MODE_PERFECT_MAC_VLAN;
-				else if (!strcmp(optarg, "perfect-tunnel"))
-					fdir_conf.mode = RTE_FDIR_MODE_PERFECT_TUNNEL;
-				else if (!strcmp(optarg, "none"))
-					fdir_conf.mode = RTE_FDIR_MODE_NONE;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "pkt-mode-invalid %s invalid - must be: "
-						 "none, signature, perfect, perfect-mac-vlan"
-						 " or perfect-tunnel\n",
-						 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "pkt-filter-mode")) {
+			if (!strcmp(optarg, "signature"))
+				fdir_conf.mode =
+					RTE_FDIR_MODE_SIGNATURE;
+			else if (!strcmp(optarg, "perfect"))
+				fdir_conf.mode = RTE_FDIR_MODE_PERFECT;
+			else if (!strcmp(optarg, "perfect-mac-vlan"))
+				fdir_conf.mode = RTE_FDIR_MODE_PERFECT_MAC_VLAN;
+			else if (!strcmp(optarg, "perfect-tunnel"))
+				fdir_conf.mode = RTE_FDIR_MODE_PERFECT_TUNNEL;
+			else if (!strcmp(optarg, "none"))
+				fdir_conf.mode = RTE_FDIR_MODE_NONE;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "pkt-mode-invalid %s invalid - must be: "
+					 "none, signature, perfect, perfect-mac-vlan"
+					 " or perfect-tunnel\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name,
-				    "pkt-filter-report-hash")) {
-				if (!strcmp(optarg, "none"))
-					fdir_conf.status =
-						RTE_FDIR_NO_REPORT_STATUS;
-				else if (!strcmp(optarg, "match"))
-					fdir_conf.status =
-						RTE_FDIR_REPORT_STATUS;
-				else if (!strcmp(optarg, "always"))
-					fdir_conf.status =
-						RTE_FDIR_REPORT_STATUS_ALWAYS;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "pkt-filter-report-hash %s invalid "
-						 "- must be: none or match or always\n",
-						 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name,
+			    "pkt-filter-report-hash")) {
+			if (!strcmp(optarg, "none"))
+				fdir_conf.status =
+					RTE_FDIR_NO_REPORT_STATUS;
+			else if (!strcmp(optarg, "match"))
+				fdir_conf.status =
+					RTE_FDIR_REPORT_STATUS;
+			else if (!strcmp(optarg, "always"))
+				fdir_conf.status =
+					RTE_FDIR_REPORT_STATUS_ALWAYS;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "pkt-filter-report-hash %s invalid "
+					 "- must be: none or match or always\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "pkt-filter-size")) {
-				if (!strcmp(optarg, "64K"))
-					fdir_conf.pballoc =
-						RTE_FDIR_PBALLOC_64K;
-				else if (!strcmp(optarg, "128K"))
-					fdir_conf.pballoc =
-						RTE_FDIR_PBALLOC_128K;
-				else if (!strcmp(optarg, "256K"))
-					fdir_conf.pballoc =
-						RTE_FDIR_PBALLOC_256K;
-				else
-					rte_exit(EXIT_FAILURE, "pkt-filter-size %s invalid -"
-						 " must be: 64K or 128K or 256K\n",
-						 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "pkt-filter-size")) {
+			if (!strcmp(optarg, "64K"))
+				fdir_conf.pballoc =
+					RTE_FDIR_PBALLOC_64K;
+			else if (!strcmp(optarg, "128K"))
+				fdir_conf.pballoc =
+					RTE_FDIR_PBALLOC_128K;
+			else if (!strcmp(optarg, "256K"))
+				fdir_conf.pballoc =
+					RTE_FDIR_PBALLOC_256K;
+			else {
+				rte_exit(EXIT_FAILURE, "pkt-filter-size %s invalid -"
+					 " must be: 64K or 128K or 256K\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name,
-				    "pkt-filter-drop-queue")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					fdir_conf.drop_queue = (uint8_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "drop queue %d invalid - must"
-						 "be >= 0 \n", n);
+		}
+		if (!strcmp(lgopts[opt_idx].name,
+			    "pkt-filter-drop-queue")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				fdir_conf.drop_queue = (uint8_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "drop queue %d invalid - must "
+					 "be >= 0\n", n);
+				return -1;
 			}
+		}
 #ifdef RTE_LIBRTE_LATENCY_STATS
-			if (!strcmp(lgopts[opt_idx].name,
-				    "latencystats")) {
-				n = atoi(optarg);
-				if (n >= 0) {
-					latencystats_lcore_id = (lcoreid_t) n;
-					latencystats_enabled = 1;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "invalid lcore id %d for latencystats"
-						 " must be >= 0\n", n);
+		if (!strcmp(lgopts[opt_idx].name,
+			    "latencystats")) {
+			n = atoi(optarg);
+			if (n >= 0) {
+				latencystats_lcore_id = (lcoreid_t) n;
+				latencystats_enabled = 1;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "invalid lcore id %d for latencystats"
+					 " must be >= 0\n", n);
+				return -1;
 			}
+		}
 #endif
 #ifdef RTE_LIBRTE_BITRATE
-			if (!strcmp(lgopts[opt_idx].name, "bitrate-stats")) {
-				n = atoi(optarg);
-				if (n >= 0) {
-					bitrate_lcore_id = (lcoreid_t) n;
-					bitrate_enabled = 1;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "invalid lcore id %d for bitrate stats"
-						 " must be >= 0\n", n);
+		if (!strcmp(lgopts[opt_idx].name, "bitrate-stats")) {
+			n = atoi(optarg);
+			if (n >= 0) {
+				bitrate_lcore_id = (lcoreid_t) n;
+				bitrate_enabled = 1;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "invalid lcore id %d for bitrate stats"
+					 " must be >= 0\n", n);
+				return -1;
 			}
+		}
 #endif
-			if (!strcmp(lgopts[opt_idx].name, "disable-crc-strip"))
-				rx_mode.hw_strip_crc = 0;
-			if (!strcmp(lgopts[opt_idx].name, "enable-lro"))
-				rx_mode.enable_lro = 1;
-			if (!strcmp(lgopts[opt_idx].name, "enable-scatter"))
-				rx_mode.enable_scatter = 1;
-			if (!strcmp(lgopts[opt_idx].name, "enable-rx-cksum"))
-				rx_mode.hw_ip_checksum = 1;
-
-			if (!strcmp(lgopts[opt_idx].name, "disable-hw-vlan")) {
-				rx_mode.hw_vlan_filter = 0;
-				rx_mode.hw_vlan_strip  = 0;
-				rx_mode.hw_vlan_extend = 0;
-			}
+		if (!strcmp(lgopts[opt_idx].name, "disable-crc-strip"))
+			rx_mode.hw_strip_crc = 0;
+		if (!strcmp(lgopts[opt_idx].name, "enable-lro"))
+			rx_mode.enable_lro = 1;
+		if (!strcmp(lgopts[opt_idx].name, "enable-scatter"))
+			rx_mode.enable_scatter = 1;
+		if (!strcmp(lgopts[opt_idx].name, "enable-rx-cksum"))
+			rx_mode.hw_ip_checksum = 1;
 
-			if (!strcmp(lgopts[opt_idx].name,
-					"disable-hw-vlan-filter"))
-				rx_mode.hw_vlan_filter = 0;
-
-			if (!strcmp(lgopts[opt_idx].name,
-					"disable-hw-vlan-strip"))
-				rx_mode.hw_vlan_strip  = 0;
-
-			if (!strcmp(lgopts[opt_idx].name,
-					"disable-hw-vlan-extend"))
-				rx_mode.hw_vlan_extend = 0;
-
-			if (!strcmp(lgopts[opt_idx].name, "enable-drop-en"))
-				rx_drop_en = 1;
-
-			if (!strcmp(lgopts[opt_idx].name, "disable-rss"))
-				rss_hf = 0;
-			if (!strcmp(lgopts[opt_idx].name, "port-topology")) {
-				if (!strcmp(optarg, "paired"))
-					port_topology = PORT_TOPOLOGY_PAIRED;
-				else if (!strcmp(optarg, "chained"))
-					port_topology = PORT_TOPOLOGY_CHAINED;
-				else if (!strcmp(optarg, "loop"))
-					port_topology = PORT_TOPOLOGY_LOOP;
-				else
-					rte_exit(EXIT_FAILURE, "port-topology %s invalid -"
-						 " must be: paired or chained \n",
-						 optarg);
+		if (!strcmp(lgopts[opt_idx].name, "disable-hw-vlan")) {
+			rx_mode.hw_vlan_filter = 0;
+			rx_mode.hw_vlan_strip  = 0;
+			rx_mode.hw_vlan_extend = 0;
+		}
+
+		if (!strcmp(lgopts[opt_idx].name,
+				"disable-hw-vlan-filter"))
+			rx_mode.hw_vlan_filter = 0;
+
+		if (!strcmp(lgopts[opt_idx].name,
+				"disable-hw-vlan-strip"))
+			rx_mode.hw_vlan_strip  = 0;
+
+		if (!strcmp(lgopts[opt_idx].name,
+				"disable-hw-vlan-extend"))
+			rx_mode.hw_vlan_extend = 0;
+
+		if (!strcmp(lgopts[opt_idx].name, "enable-drop-en"))
+			rx_drop_en = 1;
+
+		if (!strcmp(lgopts[opt_idx].name, "disable-rss"))
+			rss_hf = 0;
+		if (!strcmp(lgopts[opt_idx].name, "port-topology")) {
+			if (!strcmp(optarg, "paired"))
+				port_topology = PORT_TOPOLOGY_PAIRED;
+			else if (!strcmp(optarg, "chained"))
+				port_topology = PORT_TOPOLOGY_CHAINED;
+			else if (!strcmp(optarg, "loop"))
+				port_topology = PORT_TOPOLOGY_LOOP;
+			else {
+				rte_exit(EXIT_FAILURE, "port-topology %s invalid -"
+					 " must be: paired or chained\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "forward-mode"))
-				set_pkt_forwarding_mode(optarg);
-			if (!strcmp(lgopts[opt_idx].name, "rss-ip"))
-				rss_hf = ETH_RSS_IP;
-			if (!strcmp(lgopts[opt_idx].name, "rss-udp"))
-				rss_hf = ETH_RSS_UDP;
-			if (!strcmp(lgopts[opt_idx].name, "rxq")) {
-				n = atoi(optarg);
-				if (n >= 0 && n <= (int) MAX_QUEUE_ID)
-					nb_rxq = (queueid_t) n;
-				else
-					rte_exit(EXIT_FAILURE, "rxq %d invalid - must be"
-						  " >= 0 && <= %d\n", n,
-						  (int) MAX_QUEUE_ID);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "forward-mode"))
+			set_pkt_forwarding_mode(optarg);
+		if (!strcmp(lgopts[opt_idx].name, "rss-ip"))
+			rss_hf = ETH_RSS_IP;
+		if (!strcmp(lgopts[opt_idx].name, "rss-udp"))
+			rss_hf = ETH_RSS_UDP;
+		if (!strcmp(lgopts[opt_idx].name, "rxq")) {
+			n = atoi(optarg);
+			if (n >= 0 && n <= (int) MAX_QUEUE_ID)
+				nb_rxq = (queueid_t) n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxq %d invalid - must be"
+					  " >= 0 && <= %d\n", n,
+					  (int) MAX_QUEUE_ID);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txq")) {
-				n = atoi(optarg);
-				if (n >= 0 && n <= (int) MAX_QUEUE_ID)
-					nb_txq = (queueid_t) n;
-				else
-					rte_exit(EXIT_FAILURE, "txq %d invalid - must be"
-						  " >= 0 && <= %d\n", n,
-						  (int) MAX_QUEUE_ID);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txq")) {
+			n = atoi(optarg);
+			if (n >= 0 && n <= (int) MAX_QUEUE_ID)
+				nb_txq = (queueid_t) n;
+			else {
+				rte_exit(EXIT_FAILURE, "txq %d invalid - must be"
+					  " >= 0 && <= %d\n", n,
+					  (int) MAX_QUEUE_ID);
+				return -1;
 			}
-			if (!nb_rxq && !nb_txq) {
-				rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
-						"be non-zero\n");
+		}
+		if (!nb_rxq && !nb_txq) {
+			rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
+					"be non-zero\n");
+			return -1;
+		}
+		if (!strcmp(lgopts[opt_idx].name, "burst")) {
+			n = atoi(optarg);
+			if ((n >= 1) && (n <= MAX_PKT_BURST))
+				nb_pkt_per_burst = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "burst must >= 1 and <= %d]",
+					 MAX_PKT_BURST);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "burst")) {
-				n = atoi(optarg);
-				if ((n >= 1) && (n <= MAX_PKT_BURST))
-					nb_pkt_per_burst = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "burst must >= 1 and <= %d]",
-						 MAX_PKT_BURST);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "mbcache")) {
+			n = atoi(optarg);
+			if ((n >= 0) &&
+			    (n <= RTE_MEMPOOL_CACHE_MAX_SIZE))
+				mb_mempool_cache = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "mbcache must be >= 0 and <= %d\n",
+					 RTE_MEMPOOL_CACHE_MAX_SIZE);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "mbcache")) {
-				n = atoi(optarg);
-				if ((n >= 0) &&
-				    (n <= RTE_MEMPOOL_CACHE_MAX_SIZE))
-					mb_mempool_cache = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "mbcache must be >= 0 and <= %d\n",
-						 RTE_MEMPOOL_CACHE_MAX_SIZE);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txfreet")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_free_thresh = (int16_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txfreet must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txfreet")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_free_thresh = (int16_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txfreet must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txrst")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_rs_thresh = (int16_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txrst must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txrst")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_rs_thresh = (int16_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txrst must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txqflags")) {
+			char *end = NULL;
+
+			n = strtoul(optarg, &end, 16);
+			if (n >= 0)
+				txq_flags = (int32_t)n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "txqflags must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txqflags")) {
-				char *end = NULL;
-				n = strtoul(optarg, &end, 16);
-				if (n >= 0)
-					txq_flags = (int32_t)n;
-				else
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxd")) {
+			n = atoi(optarg);
+			if (n > 0) {
+				if (rx_free_thresh >= n)
 					rte_exit(EXIT_FAILURE,
-						 "txqflags must be >= 0\n");
+						 "rxd must be > "
+						 "rx_free_thresh(%d)\n",
+						 (int)rx_free_thresh);
+				else
+					nb_rxd = (uint16_t) n;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "rxd(%d) invalid - must be > 0\n",
+					 n);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxd")) {
-				n = atoi(optarg);
-				if (n > 0) {
-					if (rx_free_thresh >= n)
-						rte_exit(EXIT_FAILURE,
-							 "rxd must be > "
-							 "rx_free_thresh(%d)\n",
-							 (int)rx_free_thresh);
-					else
-						nb_rxd = (uint16_t) n;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "rxd(%d) invalid - must be > 0\n",
-						 n);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txd")) {
+			n = atoi(optarg);
+			if (n > 0)
+				nb_txd = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE, "txd must be in > 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txd")) {
-				n = atoi(optarg);
-				if (n > 0)
-					nb_txd = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE, "txd must be in > 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txpt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_pthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txpt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txpt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_pthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txpt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txht")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_hthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txht must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txht")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_hthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txht must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txwt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_wthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txwt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txwt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_wthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txwt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxpt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_pthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxpt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxpt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_pthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxpt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxht")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_hthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxht must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxht")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_hthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxht must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxwt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_wthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxwt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxwt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_wthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxwt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxfreet")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_free_thresh = (int16_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxfreet must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxfreet")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_free_thresh = (int16_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxfreet must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "tx-queue-stats-mapping")) {
+			if (parse_queue_stats_mapping_config(optarg, TX)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid TX queue statistics mapping config entered\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "tx-queue-stats-mapping")) {
-				if (parse_queue_stats_mapping_config(optarg, TX)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid TX queue statistics mapping config entered\n");
-				}
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rx-queue-stats-mapping")) {
+			if (parse_queue_stats_mapping_config(optarg, RX)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid RX queue statistics mapping config entered\n");
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rx-queue-stats-mapping")) {
-				if (parse_queue_stats_mapping_config(optarg, RX)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid RX queue statistics mapping config entered\n");
-				}
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txpkts")) {
+			unsigned int seg_lengths[RTE_MAX_SEGS_PER_PKT];
+			unsigned int nb_segs;
+
+			nb_segs = parse_item_list(optarg, "txpkt segments",
+					RTE_MAX_SEGS_PER_PKT, seg_lengths, 0);
+			if (nb_segs > 0)
+				set_tx_pkt_segments(seg_lengths, nb_segs);
+			else {
+				rte_exit(EXIT_FAILURE, "bad txpkts\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txpkts")) {
-				unsigned seg_lengths[RTE_MAX_SEGS_PER_PKT];
-				unsigned int nb_segs;
-
-				nb_segs = parse_item_list(optarg, "txpkt segments",
-						RTE_MAX_SEGS_PER_PKT, seg_lengths, 0);
-				if (nb_segs > 0)
-					set_tx_pkt_segments(seg_lengths, nb_segs);
-				else
-					rte_exit(EXIT_FAILURE, "bad txpkts\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "no-flush-rx"))
+			no_flush_rx = 1;
+		if (!strcmp(lgopts[opt_idx].name, "disable-link-check"))
+			no_link_check = 1;
+		if (!strcmp(lgopts[opt_idx].name, "no-lsc-interrupt"))
+			lsc_interrupt = 0;
+		if (!strcmp(lgopts[opt_idx].name, "no-rmv-interrupt"))
+			rmv_interrupt = 0;
+		if (!strcmp(lgopts[opt_idx].name, "print-event"))
+			if (parse_event_printing_config(optarg, 1)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid print-event argument\n");
+				return -1;
+			}
+		if (!strcmp(lgopts[opt_idx].name, "mask-event"))
+			if (parse_event_printing_config(optarg, 0)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid mask-event argument\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "no-flush-rx"))
-				no_flush_rx = 1;
-			if (!strcmp(lgopts[opt_idx].name, "disable-link-check"))
-				no_link_check = 1;
-			if (!strcmp(lgopts[opt_idx].name, "no-lsc-interrupt"))
-				lsc_interrupt = 0;
-			if (!strcmp(lgopts[opt_idx].name, "no-rmv-interrupt"))
-				rmv_interrupt = 0;
-			if (!strcmp(lgopts[opt_idx].name, "print-event"))
-				if (parse_event_printing_config(optarg, 1)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid print-event argument\n");
-				}
-			if (!strcmp(lgopts[opt_idx].name, "mask-event"))
-				if (parse_event_printing_config(optarg, 0)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid mask-event argument\n");
-				}
 
-			break;
-		case 'h':
-			usage(argv[0]);
-			rte_exit(EXIT_SUCCESS, "Displayed help\n");
-			break;
-		default:
-			usage(argv[0]);
-			rte_exit(EXIT_FAILURE,
-				 "Command line is incomplete or incorrect\n");
+		break;
+	case 'h':
+		usage(prgname);
+		rte_exit(EXIT_SUCCESS, "Displayed help\n");
+		return -1;
+	case 1:
+		/* does nothing*/
+		break;
+	default:
+		usage(prgname);
+		rte_exit(EXIT_FAILURE,
+			 "Command line is incomplete or incorrect\n");
+		return -1;
+	}
+	return 0;
+}
+
+void
+launch_args_parse(int argc, char **argv)
+{
+	int opt;
+	char **argvopt;
+	int opt_idx;
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, SHORTOPTS "ah",
+				 lgopts, &opt_idx)) != EOF) {
+		parse_option(opt, optarg, opt_idx, argv[0]);
+	}
+}
+
+#ifdef RTE_LIBRTE_CFGFILE_DEBUG
+#define max_nb_sections 100
+static void print_cfg(struct rte_cfgfile *cfg)
+{
+	int i, num_sections, num_entries, j;
+
+	printf("-------------------------\n");
+	printf("SECTIONS:\n\n");
+
+	char *sections[max_nb_sections+1];
+
+	for (i = 0; i < max_nb_sections; i++)
+		sections[i] = malloc(sizeof(sections[0]) * 64);
+	sections[i] = 0;
+
+	num_sections = rte_cfgfile_sections(cfg, sections, max_nb_sections);
+
+	for (i = 0; i < num_sections; i++) {
+		printf("************************\n");
+		printf("%s:\n", sections[i]);
+		num_entries = rte_cfgfile_section_num_entries(cfg, sections[i]);
+
+		struct rte_cfgfile_entry entries[num_entries];
+
+		rte_cfgfile_section_entries(cfg, sections[i],
+				entries, num_entries);
+		for (j = 0; j < num_entries; j++)
+			printf("%s -> %s\n", entries[j].name,
+					entries[j].value);
+	}
+	printf("************************\n\n");
+	printf("-------------------------\n\n");
+
+	i = 0;
+	while (sections[i]) {
+		free(sections[i]);
+		sections[i] = 0;
+		i++;
+	}
+}
+#endif
+
+#ifdef RTE_LIBRTE_CFGFILE
+static void
+non_eal_getopt(const char *str, int *opt, int *option_index)
+{
+	int i;
+
+	*opt = '?';
+	*option_index = 0;
+
+	if (strlen(str) == 1) {
+		*opt = *str;
+		return;
+	}
+
+	for (i = 0; lgopts[i].name != NULL; i++) {
+		if (strcmp(str, lgopts[i].name) == 0) {
+			*opt = lgopts[i].val;
+			*option_index = i;
 			break;
 		}
 	}
 }
+#endif
+
+#ifdef RTE_LIBRTE_CFGFILE
+#define APP_SECTION "TEST-PMD"
+int non_eal_configure(struct rte_cfgfile *cfg, char *prgname)
+{
+	int n_entries;
+	int i;
+	int opt;
+	int option_index;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+#ifdef RTE_LIBRTE_CFGFILE_DEBUG
+	print_cfg(cfg);
+#endif
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, APP_SECTION);
+
+	if (n_entries < 1) {
+		printf("No %s section entries "
+				"in cfgfile object\n", APP_SECTION);
+		return -1;
+	}
+
+	struct rte_cfgfile_entry entries[n_entries];
+
+	if (n_entries !=
+		rte_cfgfile_section_entries(cfg, APP_SECTION, entries,
+				n_entries)) {
+		rte_exit(EXIT_FAILURE, "Unexpected fault");
+		return -1;
+	}
+
+	for (i = 0; i < n_entries; i++) {
+		non_eal_getopt(entries[i].name, &opt, &option_index);
+
+		parse_option(opt, entries[i].value,
+				option_index, prgname);
+	}
+	return 0;
+}
+#endif
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index b29328a..cd69a6b 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -90,6 +90,9 @@
 #ifdef RTE_LIBRTE_LATENCY_STATS
 #include <rte_latencystats.h>
 #endif
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#endif
 
 #include "testpmd.h"
 
@@ -2251,15 +2254,66 @@ signal_handler(int signum)
 	}
 }
 
+#ifdef RTE_LIBRTE_CFGFILE
+/* Load config file path from command line */
+static char *
+cfgfile_load_path(int argc, char **argv)
+{
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		if (!strcmp("--cfgfile-path", argv[i])) {
+			if (argv[i+1] && argv[i+1][0] != '-')
+				return strdup(argv[i+1]);
+		} else if (!strncmp("--cfgfile-path=", argv[i], 15)) {
+			char *ptr = argv[i];
+
+			ptr += 15;
+			if (strlen(ptr))
+				return strdup(ptr);
+		}
+	}
+	return NULL;
+}
+#endif
+
+#define APP_NAME "TEST-PMD"
 int
 main(int argc, char** argv)
 {
 	int  diag;
 	uint8_t port_id;
+#ifdef RTE_LIBRTE_CFGFILE
+	struct rte_cfgfile *cfg = NULL;
+	char *config_file = NULL;
+#endif
 
 	signal(SIGINT, signal_handler);
 	signal(SIGTERM, signal_handler);
 
+#ifdef RTE_LIBRTE_CFGFILE
+	/* load --cfgfile-path argument from argv */
+	config_file = cfgfile_load_path(argc, argv);
+
+	if (config_file) {
+		printf("Info: found cfgfile-path \"%s\"\n", config_file);
+	} else {
+		printf("Info: not found cfgfile-path parameter "
+				"(searching for cfgfile "
+				"in default directory)\n");
+		config_file = strdup("config.ini");
+	}
+
+	cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+
+	if (cfg == NULL) {
+		printf("Info: Valid cfgfile not found\n");
+		cfg = rte_cfgfile_create(CFG_FLAG_EMPTY_VALUES);
+	}
+	diag = rte_eal_configure(cfg, argv[0]);
+	if (diag < 0)
+		rte_panic("Cannot init EAL\n");
+#endif
 	diag = rte_eal_init(argc, argv);
 	if (diag < 0)
 		rte_panic("Cannot init EAL\n");
@@ -2289,6 +2343,18 @@ main(int argc, char** argv)
 	latencystats_enabled = 0;
 #endif
 
+#ifdef RTE_LIBRTE_CFGFILE
+	if (argc > 1) {
+		non_eal_configure(cfg, argv[0]);
+		rte_cfgfile_close(cfg);
+		cfg = 0;
+
+		if (config_file) {
+			free(config_file);
+			config_file = 0;
+		}
+	}
+#endif
 	argc -= diag;
 	argv += diag;
 	if (argc > 1)
@@ -2360,6 +2426,5 @@ main(int argc, char** argv)
 		if (rc < 0)
 			return 1;
 	}
-
 	return 0;
 }
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 364502d..93eebbf 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -499,7 +499,7 @@ port_pci_reg_write(struct rte_port *port, uint32_t reg_off, uint32_t reg_v)
 unsigned int parse_item_list(char* str, const char* item_name,
 			unsigned int max_items,
 			unsigned int *parsed_items, int check_unique_values);
-void launch_args_parse(int argc, char** argv);
+void launch_args_parse(int argc, char **argv);
 void cmdline_read_from_file(const char *filename);
 void prompt(void);
 void prompt_exit(void);
@@ -641,6 +641,7 @@ enum print_warning {
 };
 int port_id_is_invalid(portid_t port_id, enum print_warning warning);
 int new_socket_id(unsigned int socket_id);
+int non_eal_configure(struct rte_cfgfile *cfg, char *prgname);
 
 /*
  * Work-around of a compilation error with ICC on invocations of the
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v2 7/7] app/testpmd: add parse arguments from JSON config file
  2017-06-26 10:59   ` [PATCH v2 0/7] Add support for using a config file for DPDK Jacek Piasecki
                       ` (5 preceding siblings ...)
  2017-06-26 10:59     ` [PATCH v2 6/7] app/testpmd: changed example to parse options from cfg file Jacek Piasecki
@ 2017-06-26 10:59     ` Jacek Piasecki
  6 siblings, 0 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-26 10:59 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Kuba Kozak

From: Kuba Kozak <kubax.kozak@intel.com>

This patch shows usage of Jansson library to parse
application arguments from JSON config file.

https://github.com/akheron/jansson

If a --cfgfile-path <path> option is passed into commandline
non EAL section, then the disired JSON file is loaded and used
by app. In case when JSON doesn't exist an INI file is loaded.
The INI file can be passed also from --cfgfile-path option.

If a file called config.ini is present in current working
directory, and no --cfgfile-path option is passed in, config.ini
file will be loaded and used by app.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test-pmd/Makefile    |  6 ++++
 app/test-pmd/config.json | 33 +++++++++++++++++
 app/test-pmd/testpmd.c   | 94 +++++++++++++++++++++++++++++++++++++++++++++++-
 config/common_base       |  5 +++
 4 files changed, 137 insertions(+), 1 deletion(-)
 create mode 100644 app/test-pmd/config.json

diff --git a/app/test-pmd/Makefile b/app/test-pmd/Makefile
index c36be19..a1c84cc 100644
--- a/app/test-pmd/Makefile
+++ b/app/test-pmd/Makefile
@@ -83,6 +83,12 @@ endif
 
 endif
 
+ifeq ($(CONFIG_RTE_JSON_SUPPORT),y)
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -ljansson
+endif
+endif
+
 CFLAGS_cmdline.o := -D_GNU_SOURCE
 
 include $(RTE_SDK)/mk/rte.app.mk
diff --git a/app/test-pmd/config.json b/app/test-pmd/config.json
new file mode 100644
index 0000000..4589dbc
--- /dev/null
+++ b/app/test-pmd/config.json
@@ -0,0 +1,33 @@
+{
+	"DPDK":
+	{
+		"v": "",
+		"l": "0-4",
+		"n": "4",
+		"master-lcore": "0",
+		"proc-type": "primary"
+	},
+	"TEST-PMD":
+	{
+		"i": "",
+		"portmask": "0xff",
+		"nb-cores": "4",
+		"port-topology": "paired"
+	},
+	"DPDK.vdev0":
+	{
+		"net_ring0": ""
+	},
+	"DPDK.vdev1":
+	{
+		"net_ring1": ""
+	},
+	"DPDK.vdev2":
+	{
+		"net_ring2": ""
+	},
+	"DPDK.vdev3":
+	{
+		"net_ring3": ""
+	}
+}
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index cd69a6b..9ce1bbe 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -46,6 +46,10 @@
 
 #include <stdint.h>
 #include <unistd.h>
+
+#ifdef RTE_JSON_SUPPORT
+#include <jansson.h>
+#endif
 #include <inttypes.h>
 
 #include <rte_common.h>
@@ -2275,6 +2279,87 @@ cfgfile_load_path(int argc, char **argv)
 	}
 	return NULL;
 }
+
+#ifdef RTE_JSON_SUPPORT
+/*
+ * Decoding JSON structure to rte_cfgfile structure.
+ * Returns handler to cfgfile object, NULL if error.
+ */
+static struct
+rte_cfgfile *l3fwd_json_to_cfg(json_t *json, int flags)
+{
+	if (!json) {
+		printf("Error: JSON structure is NULL, nothing to parse\n");
+		return NULL;
+	}
+	/* create an empty instance of cfgfile structure */
+	struct rte_cfgfile *cfgfile = rte_cfgfile_create(flags);
+
+	if (!cfgfile)
+		return NULL;
+
+	const char *section;
+	json_t *entry;
+
+	/* set pointer to first section */
+	void *iter_section = json_object_iter(json);
+
+	while (iter_section) {
+
+		section = json_object_iter_key(iter_section);
+		entry = json_object_iter_value(iter_section);
+
+		/* add parsed section name of current section to cfgfile */
+		rte_cfgfile_add_section(cfgfile, section);
+
+		/* set pointer to first entry */
+		void *iter_entry = json_object_iter(entry);
+
+		while (iter_entry) {
+
+			const char *key;
+			const char *value;
+
+			key = json_object_iter_key(iter_entry);
+			value = json_string_value(
+					json_object_iter_value(iter_entry));
+
+			/* add parsed key and value of current entry */
+			/* to cfgfile */
+			rte_cfgfile_add_entry(cfgfile, section, key, value);
+
+			/* pointer to next entry */
+			iter_entry = json_object_iter_next(entry, iter_entry);
+		}
+		/* pointer to next section */
+		iter_section = json_object_iter_next(json, iter_section);
+	}
+	return cfgfile;
+}
+
+/*
+ * Check presence and load JSON file to rte_cfgfile structure.
+ * Returns handler to cfgfile object, NULL if error.
+ */
+static struct
+rte_cfgfile *l3fwd_load_json_to_cfg(const char *path)
+{
+	struct rte_cfgfile *cfgfile = NULL;
+	/* check if config file exist */
+	if (access(path, F_OK) != -1) {
+		/* parse JSON file */
+		json_error_t error;
+		json_t *json = json_load_file(path, 0, &error);
+
+		if (json)
+			cfgfile = l3fwd_json_to_cfg(json, 0);
+		else
+			fprintf(stderr, "JSON error on line %d: %s\n",
+							error.line, error.text);
+	}
+	return cfgfile;
+}
+#endif
 #endif
 
 #define APP_NAME "TEST-PMD"
@@ -2303,9 +2388,16 @@ main(int argc, char** argv)
 				"in default directory)\n");
 		config_file = strdup("config.ini");
 	}
-
+#ifndef RTE_JSON_SUPPORT
 	cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+#endif
 
+#ifdef RTE_JSON_SUPPORT
+	if (strstr(config_file, ".ini"))
+		cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+	else if (strstr(config_file, ".json"))
+		cfg = l3fwd_load_json_to_cfg(config_file);
+#endif
 	if (cfg == NULL) {
 		printf("Info: Valid cfgfile not found\n");
 		cfg = rte_cfgfile_create(CFG_FLAG_EMPTY_VALUES);
diff --git a/config/common_base b/config/common_base
index c1d0e69..0eed278 100644
--- a/config/common_base
+++ b/config/common_base
@@ -734,3 +734,8 @@ CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n
 # Compile the crypto performance application
 #
 CONFIG_RTE_APP_CRYPTO_PERF=y
+
+#
+# Compile JSON support
+#
+CONFIG_RTE_JSON_SUPPORT=n
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [PATCH v2 1/7] cfgfile: remove EAL dependency
  2017-06-26 10:59     ` [PATCH v2 1/7] cfgfile: remove EAL dependency Jacek Piasecki
@ 2017-06-26 13:12       ` Dumitrescu, Cristian
  2017-06-27 10:26       ` [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Jacek Piasecki
  1 sibling, 0 replies; 70+ messages in thread
From: Dumitrescu, Cristian @ 2017-06-26 13:12 UTC (permalink / raw)
  To: Piasecki, JacekX, dev; +Cc: Richardson, Bruce, Jain, Deepak K, Piasecki, JacekX



> -----Original Message-----
> From: dev [mailto:dev-bounces@dpdk.org] On Behalf Of Jacek Piasecki
> Sent: Monday, June 26, 2017 11:59 AM
> To: dev@dpdk.org
> Cc: Richardson, Bruce <bruce.richardson@intel.com>; Jain, Deepak K
> <deepak.k.jain@intel.com>; Piasecki, JacekX <jacekx.piasecki@intel.com>
> Subject: [dpdk-dev] [PATCH v2 1/7] cfgfile: remove EAL dependency
> 
> This patch removes the dependency to EAL in cfgfile library.
> 
> Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
> ---
>  lib/librte_cfgfile/Makefile      |  1 +
>  lib/librte_cfgfile/rte_cfgfile.c | 29 +++++++++++++++++------------
>  2 files changed, 18 insertions(+), 12 deletions(-)
> 
> diff --git a/lib/librte_cfgfile/Makefile b/lib/librte_cfgfile/Makefile
> index 755ef11..0bee43e 100644
> --- a/lib/librte_cfgfile/Makefile
> +++ b/lib/librte_cfgfile/Makefile
> @@ -38,6 +38,7 @@ LIB = librte_cfgfile.a
> 
>  CFLAGS += -O3
>  CFLAGS += $(WERROR_FLAGS)
> +CFLAGS += -I$(SRCDIR)/../librte_eal/common/include
> 
>  EXPORT_MAP := rte_cfgfile_version.map
> 
> diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
> index b54a523..c6ae3e3 100644
> --- a/lib/librte_cfgfile/rte_cfgfile.c
> +++ b/lib/librte_cfgfile/rte_cfgfile.c
> @@ -36,7 +36,6 @@
>  #include <string.h>
>  #include <ctype.h>
>  #include <rte_common.h>
> -#include <rte_string_fns.h>
> 
>  #include "rte_cfgfile.h"
> 
> @@ -258,19 +257,25 @@ rte_cfgfile_load_with_params(const char
> *filename, int flags,
> 
>  			struct rte_cfgfile_section *sect =
>  				cfg->sections[curr_section];
> -			int n;
> +
>  			char *split[2] = {NULL};
> -			n = rte_strsplit(buffer, sizeof(buffer), split, 2, '=');
> -			if (flags & CFG_FLAG_EMPTY_VALUES) {
> -				if ((n < 1) || (n > 2)) {
> -					printf("Error at line %d - cannot split
> string, n=%d\n",
> -					       lineno, n);
> -					goto error1;
> -				}
> +			split[0] = buffer;
> +			split[1] = memchr(buffer, '=', len);
> +
> +			/* when delimeter not found */
> +			if (split[1] == NULL) {
> +				printf("Error at line %d - cannot "
> +					"split string\n", lineno);
> +				goto error1;
>  			} else {
> -				if (n != 2) {
> -					printf("Error at line %d - cannot split
> string, n=%d\n",
> -					       lineno, n);
> +				/* when delimeter found */
> +				*split[1] = '\0';
> +				split[1]++;
> +
> +				if (!(flags & CFG_FLAG_EMPTY_VALUES) &&
> +						(*split[1] == '\0')) {
> +					printf("Error at line %d - cannot "
> +						"split string\n", lineno);
>  					goto error1;
>  				}
>  			}
> --
> 2.7.4


Please separate the cfg file functionality from the eal functionality into distinct patch sets.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v3 0/4] Rework cfgfile API to enable apps config file support
  2017-06-26 10:59     ` [PATCH v2 1/7] cfgfile: remove EAL dependency Jacek Piasecki
  2017-06-26 13:12       ` Dumitrescu, Cristian
@ 2017-06-27 10:26       ` Jacek Piasecki
  2017-06-27 10:26         ` [PATCH v3 1/4] cfgfile: remove EAL dependency Jacek Piasecki
                           ` (4 more replies)
  1 sibling, 5 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-27 10:26 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Jacek Piasecki

New API for cfgfile library allows to create a cfgfile at runtime, add new
section, add entry in a section, update existing entry and save cfgfile
structure to INI file - opens up the possibility to have applications
dynamically build up a proper DPDK configuration, rather than
having to have a pre-existing one. Due the new API functions, simplification
of load() function was made. One new unit test to TEST app was added. It
contains an example of a large INI file whose parsing requires multiple
reallocation of memory.

---
v3: 
	split one patchset into two distinct patchsets:
	1. cfgfile library and TEST app changes
	2. EAL changes and examples (this patchset depends on cfgfile)
v2:
  lib eal:
  	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
	Now this function load data from cfg structure and did initial
	initialization of EAL arguments. Vdev argument are stored in different
	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
	function it is necessary to call rte_eal_init to complete EAL
	initialization. There is no more merging arguments from different
	sources (cfg file and command line).
  	Added non_eal_configure to testpmd application.
	Function maintain the same functionality as rte_eal_configure but
	for non-eal arguments. 
  	Added config JSON feature to testpmd last patch from patchset contain
	example showing use of .json configuration files.

  lib cfgfile:
  	Rework of add_section(), add_entry() new implementation
  	New members allocated_entries/sections, free_entries/sections
	in rte_cfgfile structure, change in array of pointers
	**sections, **entries instead of *sections[], *entries[]
  	Add  set_entry() to update/overwrite already existing entry in cfgfile
	struct
  	Add save() function to save on disc cfgfile structure in INI format
  	Rework of existing load() function  simplifying the code
  	Add unit test realloc_sections() in TEST app for testing realloc/malloc
	of new API functions, add test for save() function

Jacek Piasecki (3):
  cfgfile: remove EAL dependency
  cfgfile: add new functions to API
  test/cfgfile: add new unit test

Kuba Kozak (1):
  cfgfile: rework of load function

 lib/librte_cfgfile/Makefile                      |   1 +
 lib/librte_cfgfile/rte_cfgfile.c                 | 397 +++++++++++++++--------
 lib/librte_cfgfile/rte_cfgfile.h                 |  76 +++++
 lib/librte_cfgfile/rte_cfgfile_version.map       |  11 +
 test/test/test_cfgfile.c                         |  40 +++
 test/test/test_cfgfiles/etc/realloc_sections.ini | 128 ++++++++
 6 files changed, 514 insertions(+), 139 deletions(-)
 create mode 100644 test/test/test_cfgfiles/etc/realloc_sections.ini

-- 
2.7.4

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v3 1/4] cfgfile: remove EAL dependency
  2017-06-27 10:26       ` [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Jacek Piasecki
@ 2017-06-27 10:26         ` Jacek Piasecki
  2017-06-30  9:44           ` Bruce Richardson
  2017-06-27 10:26         ` [PATCH v3 2/4] cfgfile: add new functions to API Jacek Piasecki
                           ` (3 subsequent siblings)
  4 siblings, 1 reply; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-27 10:26 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Jacek Piasecki

This patch removes the dependency to EAL in cfgfile library.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 lib/librte_cfgfile/Makefile      |  1 +
 lib/librte_cfgfile/rte_cfgfile.c | 29 +++++++++++++++++------------
 2 files changed, 18 insertions(+), 12 deletions(-)

diff --git a/lib/librte_cfgfile/Makefile b/lib/librte_cfgfile/Makefile
index 755ef11..0bee43e 100644
--- a/lib/librte_cfgfile/Makefile
+++ b/lib/librte_cfgfile/Makefile
@@ -38,6 +38,7 @@ LIB = librte_cfgfile.a
 
 CFLAGS += -O3
 CFLAGS += $(WERROR_FLAGS)
+CFLAGS += -I$(SRCDIR)/../librte_eal/common/include
 
 EXPORT_MAP := rte_cfgfile_version.map
 
diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
index b54a523..c6ae3e3 100644
--- a/lib/librte_cfgfile/rte_cfgfile.c
+++ b/lib/librte_cfgfile/rte_cfgfile.c
@@ -36,7 +36,6 @@
 #include <string.h>
 #include <ctype.h>
 #include <rte_common.h>
-#include <rte_string_fns.h>
 
 #include "rte_cfgfile.h"
 
@@ -258,19 +257,25 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 
 			struct rte_cfgfile_section *sect =
 				cfg->sections[curr_section];
-			int n;
+
 			char *split[2] = {NULL};
-			n = rte_strsplit(buffer, sizeof(buffer), split, 2, '=');
-			if (flags & CFG_FLAG_EMPTY_VALUES) {
-				if ((n < 1) || (n > 2)) {
-					printf("Error at line %d - cannot split string, n=%d\n",
-					       lineno, n);
-					goto error1;
-				}
+			split[0] = buffer;
+			split[1] = memchr(buffer, '=', len);
+
+			/* when delimeter not found */
+			if (split[1] == NULL) {
+				printf("Error at line %d - cannot "
+					"split string\n", lineno);
+				goto error1;
 			} else {
-				if (n != 2) {
-					printf("Error at line %d - cannot split string, n=%d\n",
-					       lineno, n);
+				/* when delimeter found */
+				*split[1] = '\0';
+				split[1]++;
+
+				if (!(flags & CFG_FLAG_EMPTY_VALUES) &&
+						(*split[1] == '\0')) {
+					printf("Error at line %d - cannot "
+						"split string\n", lineno);
 					goto error1;
 				}
 			}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v3 2/4] cfgfile: add new functions to API
  2017-06-27 10:26       ` [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Jacek Piasecki
  2017-06-27 10:26         ` [PATCH v3 1/4] cfgfile: remove EAL dependency Jacek Piasecki
@ 2017-06-27 10:26         ` Jacek Piasecki
  2017-06-30  9:55           ` Bruce Richardson
  2017-06-30 10:28           ` Bruce Richardson
  2017-06-27 10:26         ` [PATCH v3 3/4] cfgfile: rework of load function Jacek Piasecki
                           ` (2 subsequent siblings)
  4 siblings, 2 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-27 10:26 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Jacek Piasecki

Extend existing cfgfile library with providing new API functions:

rte_cfgfile_create() - create new cfgfile object
rte_cfgfile_add_section() - add new section to existing cfgfile
object
rte_cfgfile_add_entry() - add new entry to existing cfgfile
object in specified section
rte_cfgfile_set_entry() - update existing entry in cfgfile object
rte_cfgfile_save() - save existing cfgfile object to INI file

This modification allows to create a cfgfile on
runtime and opens up the possibility to have applications
dynamically build up a proper DPDK configuration, rather than having
to have a pre-existing one.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 lib/librte_cfgfile/rte_cfgfile.c           | 249 +++++++++++++++++++++++++++--
 lib/librte_cfgfile/rte_cfgfile.h           |  76 +++++++++
 lib/librte_cfgfile/rte_cfgfile_version.map |  11 ++
 3 files changed, 323 insertions(+), 13 deletions(-)

diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
index c6ae3e3..518b6ab 100644
--- a/lib/librte_cfgfile/rte_cfgfile.c
+++ b/lib/librte_cfgfile/rte_cfgfile.c
@@ -35,6 +35,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+#include <errno.h>
 #include <rte_common.h>
 
 #include "rte_cfgfile.h"
@@ -42,13 +43,17 @@
 struct rte_cfgfile_section {
 	char name[CFG_NAME_LEN];
 	int num_entries;
-	struct rte_cfgfile_entry *entries[0];
+	int free_entries;
+	int allocated_entries;
+	struct rte_cfgfile_entry **entries;
 };
 
 struct rte_cfgfile {
 	int flags;
 	int num_sections;
-	struct rte_cfgfile_section *sections[0];
+	int free_sections;
+	int allocated_sections;
+	struct rte_cfgfile_section **sections;
 };
 
 /** when we resize a file structure, how many extra entries
@@ -104,6 +109,65 @@ _strip(char *str, unsigned len)
 	return newlen;
 }
 
+static struct rte_cfgfile_section *
+_get_section(struct rte_cfgfile *cfg, const char *sectionname)
+{
+	int i;
+
+	for (i = 0; i < cfg->num_sections; i++) {
+		if (strncmp(cfg->sections[i]->name, sectionname,
+				sizeof(cfg->sections[0]->name)) == 0)
+			return cfg->sections[i];
+	}
+	return NULL;
+}
+
+static int
+_add_entry(struct rte_cfgfile_section *section, const char *entryname,
+		const char *entryvalue)
+{
+	int i;
+
+	/* resize entry structure if we don't have room for more entries */
+	if (section->free_entries == 0) {
+
+		struct rte_cfgfile_entry **n_entries =
+				realloc(section->entries,
+				sizeof(section->entries[0]) *
+				((section->allocated_entries) +
+						CFG_ALLOC_ENTRY_BATCH));
+
+		if (n_entries == NULL)
+			return -ENOMEM;
+
+		section->entries = n_entries;
+
+		for (i = section->allocated_entries;
+				i < (section->allocated_entries) +
+						CFG_ALLOC_ENTRY_BATCH; i++) {
+			section->entries[i] =
+				malloc(sizeof(struct rte_cfgfile_entry));
+
+			if (section->entries[i] == NULL)
+				return -ENOMEM;
+		}
+		section->allocated_entries += CFG_ALLOC_ENTRY_BATCH;
+		section->free_entries += CFG_ALLOC_ENTRY_BATCH;
+	}
+
+	/* fill up entry fields with key name and value */
+	struct rte_cfgfile_entry *curr_entry =
+			section->entries[section->num_entries];
+
+	snprintf(curr_entry->name, sizeof(curr_entry->name), "%s", entryname);
+	snprintf(curr_entry->value, sizeof(curr_entry->value), "%s",
+								entryvalue);
+	section->num_entries++;
+	section->free_entries--;
+
+	return 0;
+}
+
 static int
 rte_cfgfile_check_params(const struct rte_cfgfile_parameters *params)
 {
@@ -332,6 +396,176 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 	return NULL;
 }
 
+struct rte_cfgfile *
+rte_cfgfile_create(int flags)
+{
+	int i, j;
+	struct rte_cfgfile *cfg = NULL;
+
+	cfg = malloc(sizeof(*cfg));
+	if (cfg == NULL)
+		return NULL;
+
+	memset(cfg, 0, sizeof((*cfg)));
+
+	cfg->flags = flags;
+
+	/* allocate first batch of sections and entries */
+	cfg->sections = malloc(sizeof(cfg->sections[0]) *
+						CFG_ALLOC_SECTION_BATCH);
+	if (cfg->sections == NULL)
+		return NULL;
+
+	for (i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) {
+		cfg->sections[i] = malloc(sizeof(struct rte_cfgfile_section));
+		if (cfg->sections[i] == NULL)
+			return NULL;
+
+		memset(cfg->sections[i], 0,
+					sizeof(struct rte_cfgfile_section));
+
+		cfg->sections[i]->entries =
+				malloc(sizeof(cfg->sections[i]->entries[0])
+						* CFG_ALLOC_ENTRY_BATCH);
+		if (cfg->sections[i]->entries == NULL)
+			return NULL;
+
+		for (j = 0; j < CFG_ALLOC_ENTRY_BATCH; j++) {
+			cfg->sections[i]->entries[j] = malloc(sizeof(struct
+							rte_cfgfile_entry));
+			if (cfg->sections[i]->entries[j] == NULL)
+				return NULL;
+		}
+		cfg->sections[i]->allocated_entries = CFG_ALLOC_ENTRY_BATCH;
+		cfg->sections[i]->free_entries = CFG_ALLOC_ENTRY_BATCH;
+	}
+	cfg->allocated_sections = CFG_ALLOC_SECTION_BATCH;
+	cfg->free_sections = CFG_ALLOC_SECTION_BATCH;
+
+	if (flags & CFG_FLAG_GLOBAL_SECTION)
+		rte_cfgfile_add_section(cfg, "GLOBAL");
+	return cfg;
+}
+
+int
+rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname)
+{
+	int i;
+	/* resize overall struct if we don't have room for more	sections */
+	if (cfg->free_sections == 0) {
+
+		struct rte_cfgfile_section **n_sections =
+				realloc(cfg->sections,
+				sizeof(cfg->sections[0]) *
+				((cfg->allocated_sections) +
+						CFG_ALLOC_SECTION_BATCH));
+
+		if (n_sections == NULL)
+			return -ENOMEM;
+
+		cfg->sections = n_sections;
+
+		for (i = cfg->allocated_sections;
+				i < (cfg->allocated_sections) +
+						CFG_ALLOC_SECTION_BATCH; i++) {
+			cfg->sections[i] =
+				malloc(sizeof(struct rte_cfgfile_section));
+
+			if (cfg->sections[i] == NULL)
+				return -ENOMEM;
+
+			memset(cfg->sections[i], 0,
+					sizeof(struct rte_cfgfile_section));
+		}
+		cfg->allocated_sections += CFG_ALLOC_SECTION_BATCH;
+		cfg->free_sections += CFG_ALLOC_SECTION_BATCH;
+	}
+
+	snprintf(cfg->sections[cfg->num_sections]->name,
+			sizeof(cfg->sections[0]->name), "%s", sectionname);
+	cfg->sections[cfg->num_sections]->num_entries = 0;
+
+	cfg->num_sections++;
+	cfg->free_sections--;
+
+	return 0;
+}
+
+int rte_cfgfile_add_entry(struct rte_cfgfile *cfg,
+		const char *sectionname, const char *entryname,
+		const char *entryvalue)
+{
+	int ret;
+
+	if (cfg == NULL)
+		return -EINVAL;
+
+	if (rte_cfgfile_has_entry(cfg, sectionname, entryname) != 0)
+		return -EEXIST;
+
+	/* search for section pointer by sectionname */
+	struct rte_cfgfile_section *curr_section = _get_section(cfg,
+								sectionname);
+	if (curr_section == NULL)
+		return -EINVAL;
+
+	ret = _add_entry(curr_section, entryname, entryvalue);
+
+	return ret;
+}
+
+int rte_cfgfile_set_entry(struct rte_cfgfile *cfg, const char *sectionname,
+		const char *entryname, const char *entryvalue)
+{
+	int i;
+
+	if (cfg == NULL)
+		return -EINVAL;
+
+	/* search for section pointer by sectionname */
+	struct rte_cfgfile_section *curr_section = _get_section(cfg,
+								sectionname);
+	if (curr_section == NULL)
+		return -EINVAL;
+
+	if (entryvalue == NULL)
+		entryvalue = "";
+
+	for (i = 0; i < curr_section->num_entries; i++)
+		if (!strcmp(curr_section->entries[i]->name, entryname)) {
+			strcpy(curr_section->entries[i]->value, entryvalue);
+			return 0;
+		}
+	return -1;
+}
+
+int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename)
+{
+	char buffer[CFG_NAME_LEN + CFG_VALUE_LEN + 4] = {0};
+	int i, j;
+
+	if ((cfg == NULL) || (filename == NULL))
+		return -EINVAL;
+
+	FILE *f = fopen(filename, "w");
+
+	if (f == NULL)
+		return -EINVAL;
+
+	for (i = 0; i < cfg->num_sections; i++) {
+		snprintf(buffer, sizeof(buffer), "[%s]\n",
+					cfg->sections[i]->name);
+		fputs(buffer, f);
+
+		for (j = 0; j < cfg->sections[i]->num_entries; j++) {
+			snprintf(buffer, sizeof(buffer), "%s=%s\n",
+				cfg->sections[i]->entries[j]->name,
+				cfg->sections[i]->entries[j]->value);
+			fputs(buffer, f);
+		}
+	}
+	return fclose(f);
+}
 
 int rte_cfgfile_close(struct rte_cfgfile *cfg)
 {
@@ -385,17 +619,6 @@ rte_cfgfile_sections(struct rte_cfgfile *cfg, char *sections[],
 	return i;
 }
 
-static const struct rte_cfgfile_section *
-_get_section(struct rte_cfgfile *cfg, const char *sectionname)
-{
-	int i;
-	for (i = 0; i < cfg->num_sections; i++) {
-		if (strncmp(cfg->sections[i]->name, sectionname,
-				sizeof(cfg->sections[0]->name)) == 0)
-			return cfg->sections[i];
-	}
-	return NULL;
-}
 
 int
 rte_cfgfile_has_section(struct rte_cfgfile *cfg, const char *sectionname)
diff --git a/lib/librte_cfgfile/rte_cfgfile.h b/lib/librte_cfgfile/rte_cfgfile.h
index fa10d40..6245c7e 100644
--- a/lib/librte_cfgfile/rte_cfgfile.h
+++ b/lib/librte_cfgfile/rte_cfgfile.h
@@ -121,6 +121,82 @@ struct rte_cfgfile *rte_cfgfile_load_with_params(const char *filename,
 	int flags, const struct rte_cfgfile_parameters *params);
 
 /**
+ * Create new cfgfile instance with empty sections and entries
+ *
+ * @param flags
+ *   - CFG_FLAG_GLOBAL_SECTION
+ *     Indicates that the file supports key value entries before the first
+ *     defined section.  These entries can be accessed in the "GLOBAL"
+ *     section.
+ *   - CFG_FLAG_EMPTY_VALUES
+ *     Indicates that file supports key value entries where the value can
+ *     be zero length (e.g., "key=").
+ * @return
+ *   Handle to cfgfile instance on success, NULL otherwise
+ */
+struct rte_cfgfile *rte_cfgfile_create(int flags);
+
+/**
+ * Add section in cfgfile instance.
+ *
+ * @param cfg
+ *   Pointer to the cfgfile structure.
+ * @param sectionname
+ *   Section name which will be add to cfgfile.
+ * @return
+ *   0 on success, -ENOMEM if can't add section
+ */
+int
+rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname);
+
+/**
+ * Add entry to specified section in cfgfile instance.
+ *
+ * @param cfg
+ *   Pointer to the cfgfile structure.
+ * @param sectionname
+ *   Given section name to add an entry.
+ * @param entryname
+ *   Entry name to add.
+ * @entryvalue
+ *   Entry value to add.
+ * @return
+ *   0 on success, -EEXIST if entry already exist, -EINVAL if bad argument
+ */
+int rte_cfgfile_add_entry(struct rte_cfgfile *cfg,
+		const char *sectionname, const char *entryname,
+		const char *entryvalue);
+
+/**
+ * Update value of specified entry name in given section in config file
+ *
+ * @param cfg
+ *   Config file
+ * @param sectionname
+ *   Section name
+ * @param entryname
+ *   Entry name to look for the value change
+ * @param entryvalue
+ *   New entry value. Can be also an empty string if CFG_FLAG_EMPTY_VALUES = 1
+ * @return
+ *   0 on success, -EINVAL if bad argument
+ */
+int rte_cfgfile_set_entry(struct rte_cfgfile *cfg, const char *sectionname,
+		const char *entryname, const char *entryvalue);
+
+/**
+ * Save object cfgfile to file on disc
+ *
+ * @param cfg
+ *   Config file structure
+ * @param filename
+ *   File name to save data
+ * @return
+ *   0 on success, errno otherwise
+ */
+int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename);
+
+/**
 * Get number of sections in config file
 *
 * @param cfg
diff --git a/lib/librte_cfgfile/rte_cfgfile_version.map b/lib/librte_cfgfile/rte_cfgfile_version.map
index 5fe60f7..de68ff6 100644
--- a/lib/librte_cfgfile/rte_cfgfile_version.map
+++ b/lib/librte_cfgfile/rte_cfgfile_version.map
@@ -27,3 +27,14 @@ DPDK_17.05 {
     rte_cfgfile_load_with_params;
 
 } DPDK_16.04;
+
+DPDK_17.08 {
+	global:
+
+	rte_cfgfile_add_entry;
+	rte_cfgfile_add_section;
+	rte_cfgfile_create;
+	rte_cfgfile_save;
+	rte_cfgfile_set_entry;
+
+} DPDK_17.05;
\ No newline at end of file
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v3 3/4] cfgfile: rework of load function
  2017-06-27 10:26       ` [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Jacek Piasecki
  2017-06-27 10:26         ` [PATCH v3 1/4] cfgfile: remove EAL dependency Jacek Piasecki
  2017-06-27 10:26         ` [PATCH v3 2/4] cfgfile: add new functions to API Jacek Piasecki
@ 2017-06-27 10:26         ` Jacek Piasecki
  2017-06-30 11:18           ` Bruce Richardson
  2017-06-27 10:26         ` [PATCH v3 4/4] test/cfgfile: add new unit test Jacek Piasecki
  2017-06-30 11:20         ` [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Bruce Richardson
  4 siblings, 1 reply; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-27 10:26 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Kuba Kozak, Jacek Piasecki

From: Kuba Kozak <kubax.kozak@intel.com>

New functions added to cfgfile library make it possible
to significantly simplify the code of rte_cfgfile_load_with_params()

This patch shows the new body of this function.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 lib/librte_cfgfile/rte_cfgfile.c | 143 +++++----------------------------------
 1 file changed, 17 insertions(+), 126 deletions(-)

diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
index 518b6ab..5625c80 100644
--- a/lib/librte_cfgfile/rte_cfgfile.c
+++ b/lib/librte_cfgfile/rte_cfgfile.c
@@ -207,10 +207,6 @@ struct rte_cfgfile *
 rte_cfgfile_load_with_params(const char *filename, int flags,
 			     const struct rte_cfgfile_parameters *params)
 {
-	int allocated_sections = CFG_ALLOC_SECTION_BATCH;
-	int allocated_entries = 0;
-	int curr_section = -1;
-	int curr_entry = -1;
 	char buffer[CFG_NAME_LEN + CFG_VALUE_LEN + 4] = {0};
 	int lineno = 0;
 	struct rte_cfgfile *cfg = NULL;
@@ -222,28 +218,7 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 	if (f == NULL)
 		return NULL;
 
-	cfg = malloc(sizeof(*cfg) + sizeof(cfg->sections[0]) *
-		allocated_sections);
-	if (cfg == NULL)
-		goto error2;
-
-	memset(cfg->sections, 0, sizeof(cfg->sections[0]) * allocated_sections);
-
-	if (flags & CFG_FLAG_GLOBAL_SECTION) {
-		curr_section = 0;
-		allocated_entries = CFG_ALLOC_ENTRY_BATCH;
-		cfg->sections[curr_section] = malloc(
-			sizeof(*cfg->sections[0]) +
-			sizeof(cfg->sections[0]->entries[0]) *
-			allocated_entries);
-		if (cfg->sections[curr_section] == NULL) {
-			printf("Error - no memory for global section\n");
-			goto error1;
-		}
-
-		snprintf(cfg->sections[curr_section]->name,
-				 sizeof(cfg->sections[0]->name), "GLOBAL");
-	}
+	cfg = rte_cfgfile_create(flags);
 
 	while (fgets(buffer, sizeof(buffer), f) != NULL) {
 		char *pos = NULL;
@@ -254,6 +229,7 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 					"Check if line too long\n", lineno);
 			goto error1;
 		}
+		/* skip parsing if comment character found */
 		pos = memchr(buffer, params->comment_character, len);
 		if (pos != NULL) {
 			*pos = '\0';
@@ -261,6 +237,7 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 		}
 
 		len = _strip(buffer, len);
+		/* skip lines without useful content */
 		if (buffer[0] != '[' && memchr(buffer, '=', len) == NULL)
 			continue;
 
@@ -268,130 +245,44 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 			/* section heading line */
 			char *end = memchr(buffer, ']', len);
 			if (end == NULL) {
-				printf("Error line %d - no terminating '['"
+				printf("Error line %d - no terminating ']'"
 					"character found\n", lineno);
 				goto error1;
 			}
 			*end = '\0';
 			_strip(&buffer[1], end - &buffer[1]);
 
-			/* close off old section and add start new one */
-			if (curr_section >= 0)
-				cfg->sections[curr_section]->num_entries =
-					curr_entry + 1;
-			curr_section++;
-
-			/* resize overall struct if we don't have room for more
-			sections */
-			if (curr_section == allocated_sections) {
-				allocated_sections += CFG_ALLOC_SECTION_BATCH;
-				struct rte_cfgfile *n_cfg = realloc(cfg,
-					sizeof(*cfg) + sizeof(cfg->sections[0])
-					* allocated_sections);
-				if (n_cfg == NULL) {
-					curr_section--;
-					printf("Error - no more memory\n");
-					goto error1;
-				}
-				cfg = n_cfg;
-			}
-
-			/* allocate space for new section */
-			allocated_entries = CFG_ALLOC_ENTRY_BATCH;
-			curr_entry = -1;
-			cfg->sections[curr_section] = malloc(
-				sizeof(*cfg->sections[0]) +
-				sizeof(cfg->sections[0]->entries[0]) *
-				allocated_entries);
-			if (cfg->sections[curr_section] == NULL) {
-				printf("Error - no more memory\n");
-				goto error1;
-			}
-
-			snprintf(cfg->sections[curr_section]->name,
-					sizeof(cfg->sections[0]->name),
-					"%s", &buffer[1]);
+			rte_cfgfile_add_section(cfg, &buffer[1]);
 		} else {
-			/* value line */
-			if (curr_section < 0) {
-				printf("Error line %d - value outside of"
-					"section\n", lineno);
-				goto error1;
-			}
-
-			struct rte_cfgfile_section *sect =
-				cfg->sections[curr_section];
-
+			/* key and value line */
 			char *split[2] = {NULL};
+
 			split[0] = buffer;
 			split[1] = memchr(buffer, '=', len);
+			*split[1] = '\0';
+			split[1]++;
+
+			_strip(split[0], strlen(split[0]));
+			_strip(split[1], strlen(split[1]));
 
-			/* when delimeter not found */
-			if (split[1] == NULL) {
+			if (!(flags & CFG_FLAG_EMPTY_VALUES) &&
+					(*split[1] == '\0')) {
 				printf("Error at line %d - cannot "
 					"split string\n", lineno);
 				goto error1;
-			} else {
-				/* when delimeter found */
-				*split[1] = '\0';
-				split[1]++;
-
-				if (!(flags & CFG_FLAG_EMPTY_VALUES) &&
-						(*split[1] == '\0')) {
-					printf("Error at line %d - cannot "
-						"split string\n", lineno);
-					goto error1;
-				}
 			}
 
-			curr_entry++;
-			if (curr_entry == allocated_entries) {
-				allocated_entries += CFG_ALLOC_ENTRY_BATCH;
-				struct rte_cfgfile_section *n_sect = realloc(
-					sect, sizeof(*sect) +
-					sizeof(sect->entries[0]) *
-					allocated_entries);
-				if (n_sect == NULL) {
-					curr_entry--;
-					printf("Error - no more memory\n");
-					goto error1;
-				}
-				sect = cfg->sections[curr_section] = n_sect;
-			}
-
-			sect->entries[curr_entry] = malloc(
-				sizeof(*sect->entries[0]));
-			if (sect->entries[curr_entry] == NULL) {
-				printf("Error - no more memory\n");
+			if (cfg->num_sections == 0)
 				goto error1;
-			}
 
-			struct rte_cfgfile_entry *entry = sect->entries[
-				curr_entry];
-			snprintf(entry->name, sizeof(entry->name), "%s",
-				split[0]);
-			snprintf(entry->value, sizeof(entry->value), "%s",
-				 split[1] ? split[1] : "");
-			_strip(entry->name, strnlen(entry->name,
-				sizeof(entry->name)));
-			_strip(entry->value, strnlen(entry->value,
-				sizeof(entry->value)));
+			_add_entry(cfg->sections[cfg->num_sections - 1],
+					split[0], (split[1] ? split[1] : ""));
 		}
 	}
 	fclose(f);
-	cfg->flags = flags;
-	cfg->num_sections = curr_section + 1;
-	/* curr_section will still be -1 if we have an empty file */
-	if (curr_section >= 0)
-		cfg->sections[curr_section]->num_entries = curr_entry + 1;
 	return cfg;
-
 error1:
-	cfg->num_sections = curr_section + 1;
-	if (curr_section >= 0)
-		cfg->sections[curr_section]->num_entries = curr_entry + 1;
 	rte_cfgfile_close(cfg);
-error2:
 	fclose(f);
 	return NULL;
 }
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v3 4/4] test/cfgfile: add new unit test
  2017-06-27 10:26       ` [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Jacek Piasecki
                           ` (2 preceding siblings ...)
  2017-06-27 10:26         ` [PATCH v3 3/4] cfgfile: rework of load function Jacek Piasecki
@ 2017-06-27 10:26         ` Jacek Piasecki
  2017-06-30 11:20         ` [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Bruce Richardson
  4 siblings, 0 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-27 10:26 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Jacek Piasecki

Load huge realloc_sections.ini file to check malloc/realloc
ability of cfgfile library.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 test/test/test_cfgfile.c                         |  40 +++++++
 test/test/test_cfgfiles/etc/realloc_sections.ini | 128 +++++++++++++++++++++++
 2 files changed, 168 insertions(+)
 create mode 100644 test/test/test_cfgfiles/etc/realloc_sections.ini

diff --git a/test/test/test_cfgfile.c b/test/test/test_cfgfile.c
index 4cc9b14..2278618 100644
--- a/test/test/test_cfgfile.c
+++ b/test/test/test_cfgfile.c
@@ -111,6 +111,7 @@ _test_cfgfile_sample(struct rte_cfgfile *cfgfile)
 	return 0;
 }
 
+
 static int
 test_cfgfile_sample1(void)
 {
@@ -154,6 +155,42 @@ test_cfgfile_sample2(void)
 }
 
 static int
+test_cfgfile_realloc_sections(void)
+{
+	struct rte_cfgfile *cfgfile;
+	int ret;
+	const char *value;
+
+	cfgfile = rte_cfgfile_load(CFG_FILES_ETC "/realloc_sections.ini", 0);
+	TEST_ASSERT_NOT_NULL(cfgfile, "Failed to load config file");
+
+	ret = rte_cfgfile_num_sections(cfgfile, NULL, 0);
+	TEST_ASSERT(ret == 9, "Unexpected number of sections: %d", ret);
+
+	ret = rte_cfgfile_has_section(cfgfile, "section9");
+	TEST_ASSERT(ret, "section9 missing");
+
+	ret = rte_cfgfile_section_num_entries(cfgfile, "section3");
+	TEST_ASSERT(ret == 21,
+			"section3 unexpected number of entries: %d", ret);
+
+	ret = rte_cfgfile_section_num_entries(cfgfile, "section9");
+	TEST_ASSERT(ret == 8, "section9 unexpected number of entries: %d", ret);
+
+	value = rte_cfgfile_get_entry(cfgfile, "section9", "key8");
+	TEST_ASSERT(strcmp("value8_section9", value) == 0,
+		    "key unexpected value: %s", value);
+
+	ret = rte_cfgfile_save(cfgfile, "cfgfile_save.ini");
+	TEST_ASSERT_SUCCESS(ret, "Failed to save *.ini file");
+
+	ret = rte_cfgfile_close(cfgfile);
+	TEST_ASSERT_SUCCESS(ret, "Failed to close cfgfile");
+
+	return 0;
+}
+
+static int
 test_cfgfile_invalid_section_header(void)
 {
 	struct rte_cfgfile *cfgfile;
@@ -292,6 +329,9 @@ test_cfgfile(void)
 	if (test_cfgfile_sample2())
 		return -1;
 
+	if (test_cfgfile_realloc_sections())
+		return -1;
+
 	if (test_cfgfile_invalid_section_header())
 		return -1;
 
diff --git a/test/test/test_cfgfiles/etc/realloc_sections.ini b/test/test/test_cfgfiles/etc/realloc_sections.ini
new file mode 100644
index 0000000..e653e40
--- /dev/null
+++ b/test/test/test_cfgfiles/etc/realloc_sections.ini
@@ -0,0 +1,128 @@
+[section1]
+key1=value1_section1
+key2=value2_section1
+key3=value3_section1
+key4=value4_section1
+key5=value5_section1
+key6=value6_section1
+key7=value7_section1
+key8=value8_section1
+key9=value9_section1
+key10=value10_section1
+key11=value11_section1
+key12=value12_section1
+key13=value13_section1
+key14=value14_section1
+key15=value15_section1
+key16=value16_section1
+key17=value17_section1
+key18=value18_section1
+key19=value19_section1
+key20=value20_section1
+key21=value21_section1
+
+[section2]
+key1=value1_section2
+key2=value2_section2
+key3=value3_section2
+key4=value4_section2
+key5=value5_section2
+key6=value6_section2
+key7=value7_section2
+key8=value8_section2
+key9=value9_section2
+key10=value10_section2
+key11=value11_section2
+key12=value12_section2
+key13=value13_section2
+key14=value14_section2
+key15=value15_section2
+key16=value16_section2
+key17=value17_section2
+key18=value18_section2
+key19=value19_section2
+key20=value20_section2
+key21=value21_section2
+
+[section3]
+key1=value1_section3
+key2=value2_section3
+key3=value3_section3
+key4=value4_section3
+key5=value5_section3
+key6=value6_section3
+key7=value7_section3
+key8=value8_section3
+key9=value9_section3
+key10=value10_section3
+key11=value11_section3
+key12=value12_section3
+key13=value13_section3
+key14=value14_section3
+key15=value15_section3
+key16=value16_section3
+key17=value17_section3
+key18=value18_section3
+key19=value19_section3
+key20=value20_section3
+key21=value21_section3
+
+[section4]
+key1=value1_section4
+key2=value2_section4
+key3=value3_section4
+key4=value4_section4
+key5=value5_section4
+key6=value6_section4
+key7=value7_section4
+key8=value8_section4
+
+[section5]
+key1=value1_section5
+key2=value2_section5
+key3=value3_section5
+key4=value4_section5
+key5=value5_section5
+key6=value6_section5
+key7=value7_section5
+key8=value8_section5
+
+[section6]
+key1=value1_section6
+key2=value2_section6
+key3=value3_section6
+key4=value4_section6
+key5=value5_section6
+key6=value6_section6
+key7=value7_section6
+key8=value8_section6
+
+[section7]
+key1=value1_section7
+key2=value2_section7
+key3=value3_section7
+key4=value4_section7
+key5=value5_section7
+key6=value6_section7
+key7=value7_section7
+key8=value8_section7
+
+[section8]
+key1=value1_section8
+key2=value2_section8
+key3=value3_section8
+key4=value4_section8
+key5=value5_section8
+key6=value6_section8
+key7=value7_section8
+key8=value8_section8
+
+[section9]
+key1=value1_section9
+key2=value2_section9
+key3=value3_section9
+key4=value4_section9
+key5=value5_section9
+key6=value6_section9
+key7=value7_section9
+key8=value8_section9
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v3 0/3] EAL change for using a config file for DPDK
  2017-06-26 10:59     ` [PATCH v2 5/7] eal: add functions parsing EAL arguments Jacek Piasecki
@ 2017-06-27 10:52       ` Jacek Piasecki
  2017-06-27 10:52         ` [PATCH v3 1/3] eal: add functions parsing EAL arguments Jacek Piasecki
                           ` (3 more replies)
  0 siblings, 4 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-27 10:52 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Jacek Piasecki

This patchset introduce a mechanism for running dpdk application with parameters
provided by configuration file.

A new API for EAL takes a config file data type - either loaded from file,
or built up programmatically in the application - and extracts DPDK parameters
from it to be used when eal init is called. This allows apps to have
an alternative method to configure EAL, other than via command-line parameters.

Testpmd application is used to the demonstrate the new eal API.
If a --cfgfile-path <path> option is passed into command line non EAL section,
then the file is loaded and used by app. If a file called config.ini
is present in current working directory, and no --cfgfile-path option is
passed in, config.ini file will be loaded and used by app.

Last patch demonstrates the usage of JSON file format insted of config.ini
JSON file can be called the same way as above, thru --cfgfile-path <path>

---
v3: 
	split one patchset into two distinct patchsets:
	1. cfgfile library and TEST app changes
	2. EAL changes and examples (this patchset depends on cfgfile)
v2:
  lib eal:
  	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
	Now this function load data from cfg structure and did initial
	initialization of EAL arguments. Vdev argument are stored in different
	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
	function it is necessary to call rte_eal_init to complete EAL
	initialization. There is no more merging arguments from different
	sources (cfg file and command line).
  	Added non_eal_configure to testpmd application.
	Function maintain the same functionality as rte_eal_configure but
	for non-eal arguments. 
  	Added config JSON feature to testpmd last patch from patchset contain
	example showing use of .json configuration files.

  lib cfgfile:
  	Rework of add_section(), add_entry() new implementation
  	New members allocated_entries/sections, free_entries/sections
	in rte_cfgfile structure, change in array of pointers
	**sections, **entries instead of *sections[], *entries[]
  	Add  set_entry() to update/overwrite already existing entry in cfgfile
	struct
  	Add save() function to save on disc cfgfile structure in INI format
  	Rework of existing load() function  simplifying the code
  	Add unit test realloc_sections() in TEST app for testing realloc/malloc
	of new API functions, add test for save() function

Kuba Kozak (3):
  eal: add functions parsing EAL arguments
  app/testpmd: changed example to parse options from cfg file
  app/testpmd: add parse arguments from JSON config file

 app/test-pmd/Makefile                           |    6 +
 app/test-pmd/config.ini                         |   24 +
 app/test-pmd/config.json                        |   33 +
 app/test-pmd/parameters.c                       | 1193 +++++++++++++----------
 app/test-pmd/testpmd.c                          |  159 ++-
 app/test-pmd/testpmd.h                          |    3 +-
 config/common_base                              |    6 +
 lib/Makefile                                    |    6 +-
 lib/librte_eal/bsdapp/eal/Makefile              |    4 +
 lib/librte_eal/bsdapp/eal/eal.c                 |  249 ++++-
 lib/librte_eal/bsdapp/eal/rte_eal_version.map   |    4 +
 lib/librte_eal/common/eal_common_cpuflags.c     |   14 +-
 lib/librte_eal/common/eal_common_lcore.c        |   11 +-
 lib/librte_eal/common/eal_common_options.c      |    5 +
 lib/librte_eal/common/include/rte_eal.h         |   21 +
 lib/librte_eal/linuxapp/eal/Makefile            |    3 +
 lib/librte_eal/linuxapp/eal/eal.c               |  353 +++++--
 lib/librte_eal/linuxapp/eal/rte_eal_version.map |    4 +
 mk/rte.app.mk                                   |    2 +-
 19 files changed, 1469 insertions(+), 631 deletions(-)
 create mode 100644 app/test-pmd/config.ini
 create mode 100644 app/test-pmd/config.json

-- 
2.7.4

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v3 1/3] eal: add functions parsing EAL arguments
  2017-06-27 10:52       ` [PATCH v3 0/3] EAL change for using a config file for DPDK Jacek Piasecki
@ 2017-06-27 10:52         ` Jacek Piasecki
  2017-06-30 16:04           ` Bruce Richardson
                             ` (2 more replies)
  2017-06-27 10:52         ` [PATCH v3 2/3] app/testpmd: changed example to parse options from " Jacek Piasecki
                           ` (2 subsequent siblings)
  3 siblings, 3 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-27 10:52 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Kuba Kozak

From: Kuba Kozak <kubax.kozak@intel.com>

added function rte_eal_configure which configure
Environment Abstraction Layer (EAL) using
configuration structure.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
This patch depends on cfgfile patchset with:
"cfgfile: remove EAL dependency"
"cfgfile: add new functions to API"
"cfgfile: rework of load function"
"test/cfgfile: add new unit test"
---
 config/common_base                              |   1 +
 lib/Makefile                                    |   6 +-
 lib/librte_eal/bsdapp/eal/Makefile              |   4 +
 lib/librte_eal/bsdapp/eal/eal.c                 | 249 ++++++++++++++---
 lib/librte_eal/bsdapp/eal/rte_eal_version.map   |   4 +
 lib/librte_eal/common/eal_common_cpuflags.c     |  14 +-
 lib/librte_eal/common/eal_common_lcore.c        |  11 +-
 lib/librte_eal/common/eal_common_options.c      |   5 +
 lib/librte_eal/common/include/rte_eal.h         |  21 ++
 lib/librte_eal/linuxapp/eal/Makefile            |   3 +
 lib/librte_eal/linuxapp/eal/eal.c               | 353 ++++++++++++++++++------
 lib/librte_eal/linuxapp/eal/rte_eal_version.map |   4 +
 mk/rte.app.mk                                   |   2 +-
 13 files changed, 543 insertions(+), 134 deletions(-)

diff --git a/config/common_base b/config/common_base
index f6aafd1..c1d0e69 100644
--- a/config/common_base
+++ b/config/common_base
@@ -569,6 +569,7 @@ CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
 # Compile librte_cfgfile
 #
 CONFIG_RTE_LIBRTE_CFGFILE=y
+CONFIG_RTE_LIBRTE_CFGFILE_DEBUG=n
 
 #
 # Compile librte_cmdline
diff --git a/lib/Makefile b/lib/Makefile
index 07e1fd0..fc5df3a 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -32,7 +32,11 @@
 include $(RTE_SDK)/mk/rte.vars.mk
 
 DIRS-y += librte_compat
+DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile
 DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+DEPDIRS-librte_eal := librte_cfgfile
+endif
 DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
 DEPDIRS-librte_ring := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
@@ -41,8 +45,6 @@ DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
 DEPDIRS-librte_mbuf := librte_eal librte_mempool
 DIRS-$(CONFIG_RTE_LIBRTE_TIMER) += librte_timer
 DEPDIRS-librte_timer := librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile
-DEPDIRS-librte_cfgfile := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_CMDLINE) += librte_cmdline
 DEPDIRS-librte_cmdline := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_ETHER) += librte_ether
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index a0f9950..d70eefb 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -50,6 +50,10 @@ EXPORT_MAP := rte_eal_version.map
 
 LIBABIVER := 4
 
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -lrte_cfgfile
+endif
+
 # specific to bsdapp exec-env
 SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) := eal.c
 SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += eal_memory.c
diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index 05f0c1f..7baf848 100644
--- a/lib/librte_eal/bsdapp/eal/eal.c
+++ b/lib/librte_eal/bsdapp/eal/eal.c
@@ -73,6 +73,7 @@
 #include <rte_version.h>
 #include <rte_atomic.h>
 #include <malloc_heap.h>
+#include <rte_cfgfile.h>
 
 #include "eal_private.h"
 #include "eal_thread.h"
@@ -309,6 +310,18 @@ eal_get_hugepage_mem_size(void)
 
 /* Parse the arguments for --log-level only */
 static void
+eal_log_level_cfg(struct rte_cfgfile *cfg)
+{
+	const char *entry;
+
+	entry = rte_cfgfile_get_entry(cfg, "DPDK", OPT_LOG_LEVEL);
+	if (entry)
+		eal_parse_common_option(OPT_LOG_LEVEL_NUM, entry,
+				&internal_config);
+}
+
+/* Parse the arguments for --log-level only */
+static void
 eal_log_level_parse(int argc, char **argv)
 {
 	int opt;
@@ -347,6 +360,58 @@ eal_log_level_parse(int argc, char **argv)
 	optarg = old_optarg;
 }
 
+/* Parse single argument */
+static int
+eal_parse_option(int opt, char *optarg, int option_index, char *prgname)
+{
+	int ret;
+
+	/* getopt is not happy, stop right now */
+	if (opt == '?') {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+
+	ret = eal_parse_common_option(opt, optarg, &internal_config);
+	/* common parser is not happy */
+	if (ret < 0) {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+	/* common parser handled this option */
+	if (ret == 0)
+		return 0;
+
+	switch (opt) {
+	case 'h':
+		eal_usage(prgname);
+		exit(EXIT_SUCCESS);
+		break;
+
+	default:
+		if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
+			RTE_LOG(ERR, EAL, "Option %c is not supported "
+				"on FreeBSD\n", opt);
+		} else if (opt >= OPT_LONG_MIN_NUM &&
+			   opt < OPT_LONG_MAX_NUM) {
+			RTE_LOG(ERR, EAL, "Option %s is not supported "
+				"on FreeBSD\n",
+				eal_long_options[option_index].name);
+		} else {
+			RTE_LOG(ERR, EAL, "Option %d is not supported "
+				"on FreeBSD\n", opt);
+		}
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+	return 0;
+out:
+	return ret;
+}
+
 /* Parse the argument given in the command line of the application */
 static int
 eal_parse_args(int argc, char **argv)
@@ -367,45 +432,9 @@ eal_parse_args(int argc, char **argv)
 	while ((opt = getopt_long(argc, argvopt, eal_short_options,
 				  eal_long_options, &option_index)) != EOF) {
 
-		/* getopt is not happy, stop right now */
-		if (opt == '?') {
-			eal_usage(prgname);
-			ret = -1;
-			goto out;
-		}
-
-		ret = eal_parse_common_option(opt, optarg, &internal_config);
-		/* common parser is not happy */
-		if (ret < 0) {
-			eal_usage(prgname);
-			ret = -1;
-			goto out;
-		}
-		/* common parser handled this option */
-		if (ret == 0)
-			continue;
-
-		switch (opt) {
-		case 'h':
-			eal_usage(prgname);
-			exit(EXIT_SUCCESS);
-		default:
-			if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
-				RTE_LOG(ERR, EAL, "Option %c is not supported "
-					"on FreeBSD\n", opt);
-			} else if (opt >= OPT_LONG_MIN_NUM &&
-				   opt < OPT_LONG_MAX_NUM) {
-				RTE_LOG(ERR, EAL, "Option %s is not supported "
-					"on FreeBSD\n",
-					eal_long_options[option_index].name);
-			} else {
-				RTE_LOG(ERR, EAL, "Option %d is not supported "
-					"on FreeBSD\n", opt);
-			}
-			eal_usage(prgname);
-			ret = -1;
+		ret = eal_parse_option(opt, optarg, option_index, prgname);
+		if (ret < 0)
 			goto out;
-		}
 	}
 
 	if (eal_adjust_config(&internal_config) != 0) {
@@ -677,3 +706,147 @@ rte_eal_process_type(void)
 {
 	return rte_config.process_type;
 }
+
+#ifdef RTE_LIBRTE_CFGFILE
+#define vdev_buff_size		200
+#define sectionname_size	20
+static int
+parse_vdev_devices(struct rte_cfgfile *cfg)
+{
+	char sectionname[sectionname_size];
+	char buffer1[vdev_buff_size];
+	int vdev_nb = 0;
+	int n_entries;
+	int i;
+
+	/* ----------- parsing VDEVS */
+	snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
+
+	for (vdev_nb = 1; rte_cfgfile_has_section(cfg, sectionname);
+			vdev_nb++) {
+		n_entries = rte_cfgfile_section_num_entries(cfg, sectionname);
+
+		struct rte_cfgfile_entry entries[n_entries];
+
+		if (n_entries != rte_cfgfile_section_entries(cfg, sectionname,
+				entries, n_entries)) {
+			rte_eal_init_alert("Unexpected fault.");
+			rte_errno = EFAULT;
+			return -1;
+		}
+
+		buffer1[0] = 0;
+		for (i = 0; i < n_entries; i++) {
+			if (strlen(entries[i].value)) {
+
+				if ((strlen(buffer1) +
+						strlen(entries[i].name) +
+						strlen(entries[i].value) + 3)
+						>= vdev_buff_size)
+					goto buff_size_err;
+				strcat(buffer1, entries[i].name);
+				strcat(buffer1, "=");
+				strcat(buffer1, entries[i].value);
+			} else {
+				if ((strlen(buffer1) +
+						strlen(entries[i].name) + 2)
+						>= vdev_buff_size)
+					goto buff_size_err;
+				strcat(buffer1, entries[i].name);
+			}
+
+			if (i < (n_entries - 1))
+				strcat(buffer1, ",");
+		}
+
+		/* parsing vdev */
+		if (rte_eal_devargs_add(RTE_DEVTYPE_VIRTUAL,
+				buffer1) < 0) {
+			return -1;
+		}
+		snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
+	}
+	/* ----------- parsing VDEVS */
+	return 0;
+
+buff_size_err:
+	printf("parse_vdev_devices(): buffer size is to small\n");
+	return -1;
+}
+
+static void
+eal_getopt(const char *str, int *opt, int *option_index)
+{
+	int i;
+
+	*opt = '?';
+	*option_index = 0;
+
+	if (strlen(str) == 1) {
+		*opt = *str;
+		return;
+	}
+
+	for (i = 0; eal_long_options[i].name != NULL; i++) {
+		if (strcmp(str, eal_long_options[i].name) == 0) {
+			*opt = eal_long_options[i].val;
+			*option_index = i;
+			break;
+		}
+	}
+}
+
+int
+rte_eal_configure(struct rte_cfgfile *cfg, char *prgname)
+{
+	int n_entries;
+	int i;
+	int opt;
+	int option_index;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, "DPDK");
+
+	if (n_entries < 1) {
+		printf("No DPDK section entries in cfgfile object\n");
+		return 0;
+	}
+
+	struct rte_cfgfile_entry entries[n_entries];
+
+	if (n_entries !=
+			rte_cfgfile_section_entries(cfg, "DPDK", entries,
+					n_entries)) {
+		rte_eal_init_alert("Unexpected fault.");
+		rte_errno = EFAULT;
+		return -1;
+	}
+
+	eal_reset_internal_config(&internal_config);
+
+	/* set log level as early as possible */
+	eal_log_level_cfg(cfg);
+
+	for (i = 0; i < n_entries; i++) {
+		eal_getopt(entries[i].name, &opt, &option_index);
+
+		if (eal_parse_option(opt, entries[i].value,
+				option_index, prgname) != 0) {
+			rte_eal_init_alert("Invalid config file arguments.");
+			rte_errno = EINVAL;
+			return -1;
+		}
+	}
+
+	if (parse_vdev_devices(cfg) < 0) {
+		rte_eal_init_alert("Couldn't parse vdevs");
+		rte_errno = ENOMEM;
+		return -1;
+	}
+	return 0;
+}
+#endif
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 2e48a73..a939b03 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -193,3 +193,7 @@ DPDK_17.05 {
 	vfio_get_group_no;
 
 } DPDK_17.02;
+
+DPDK_17.08 {
+	rte_eal_configure;
+} DPDK_17.05;
diff --git a/lib/librte_eal/common/eal_common_cpuflags.c b/lib/librte_eal/common/eal_common_cpuflags.c
index 9a2d080..6a365f3 100644
--- a/lib/librte_eal/common/eal_common_cpuflags.c
+++ b/lib/librte_eal/common/eal_common_cpuflags.c
@@ -50,12 +50,18 @@ rte_cpu_check_supported(void)
 int
 rte_cpu_is_supported(void)
 {
+	static int run_once;
+	static int ret;
 	/* This is generated at compile-time by the build system */
 	static const enum rte_cpu_flag_t compile_time_flags[] = {
 			RTE_COMPILE_TIME_CPUFLAGS
 	};
 	unsigned count = RTE_DIM(compile_time_flags), i;
-	int ret;
+
+	/* No need to calculate this function again if we know the result */
+	if (run_once)
+		return ret;
+	run_once = 1;
 
 	for (i = 0; i < count; i++) {
 		ret = rte_cpu_get_flag_enabled(compile_time_flags[i]);
@@ -64,16 +70,16 @@ rte_cpu_is_supported(void)
 			fprintf(stderr,
 				"ERROR: CPU feature flag lookup failed with error %d\n",
 				ret);
-			return 0;
+			return ret = 0;
 		}
 		if (!ret) {
 			fprintf(stderr,
 			        "ERROR: This system does not support \"%s\".\n"
 			        "Please check that RTE_MACHINE is set correctly.\n",
 			        rte_cpu_get_flag_name(compile_time_flags[i]));
-			return 0;
+			return ret = 0;
 		}
 	}
 
-	return 1;
+	return ret = 1;
 }
diff --git a/lib/librte_eal/common/eal_common_lcore.c b/lib/librte_eal/common/eal_common_lcore.c
index 84fa0cb..ce3ef34 100644
--- a/lib/librte_eal/common/eal_common_lcore.c
+++ b/lib/librte_eal/common/eal_common_lcore.c
@@ -53,11 +53,18 @@
 int
 rte_eal_cpu_init(void)
 {
+	static int run_once;
+	static int ret;
 	/* pointer to global configuration */
 	struct rte_config *config = rte_eal_get_configuration();
 	unsigned lcore_id;
 	unsigned count = 0;
 
+	/* No need to calculate this function again if we know the result */
+	if (run_once)
+		return ret;
+	run_once = 1;
+
 	/*
 	 * Parse the maximum set of logical cores, detect the subset of running
 	 * ones and enable them by default.
@@ -91,7 +98,7 @@ rte_eal_cpu_init(void)
 				"RTE_MAX_NUMA_NODES (%d)\n",
 				lcore_config[lcore_id].socket_id,
 				RTE_MAX_NUMA_NODES);
-			return -1;
+			return ret = -1;
 #endif
 		}
 		RTE_LOG(DEBUG, EAL, "Detected lcore %u as "
@@ -107,5 +114,5 @@ rte_eal_cpu_init(void)
 		RTE_MAX_LCORE);
 	RTE_LOG(INFO, EAL, "Detected %u lcore(s)\n", config->lcore_count);
 
-	return 0;
+	return ret = 0;
 }
diff --git a/lib/librte_eal/common/eal_common_options.c b/lib/librte_eal/common/eal_common_options.c
index f470195..138a27f 100644
--- a/lib/librte_eal/common/eal_common_options.c
+++ b/lib/librte_eal/common/eal_common_options.c
@@ -131,8 +131,13 @@ static int core_parsed;
 void
 eal_reset_internal_config(struct internal_config *internal_cfg)
 {
+	static int run_once;
 	int i;
 
+	if (run_once)
+		return;
+	run_once = 1;
+
 	internal_cfg->memory = 0;
 	internal_cfg->force_nrank = 0;
 	internal_cfg->force_nchannel = 0;
diff --git a/lib/librte_eal/common/include/rte_eal.h b/lib/librte_eal/common/include/rte_eal.h
index abf020b..e0705de 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -46,6 +46,8 @@
 #include <rte_per_lcore.h>
 #include <rte_config.h>
 
+struct rte_cfgfile; /* forward declaration of struct */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -188,6 +190,25 @@ int rte_eal_iopl_init(void);
  */
 int rte_eal_init(int argc, char **argv);
 
+#ifdef RTE_LIBRTE_CFGFILE
+/**
+ * Initialize the Environment Abstraction Layer (EAL) using
+ * configuration structure
+ *
+ * @param cfg
+ *   pointer to config file structure.
+ *   If 0 - function free allocated for **cfg_argv memory
+ * @param prgname
+ *   pointer to string with execution path
+ *
+ * @return
+ *  - On success, return 0
+ *  - On failure, returns -1.
+ */
+int
+rte_eal_configure(struct rte_cfgfile *cfg, char *prgname);
+#endif
+
 /**
  * Check if a primary process is currently alive
  *
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 640afd0..656033e 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -50,6 +50,9 @@ LDLIBS += -ldl
 LDLIBS += -lpthread
 LDLIBS += -lgcc_s
 LDLIBS += -lrt
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -lrte_cfgfile
+endif
 
 # specific to linuxapp exec-env
 SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) := eal.c
diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
index 7c78f2d..f5973f4 100644
--- a/lib/librte_eal/linuxapp/eal/eal.c
+++ b/lib/librte_eal/linuxapp/eal/eal.c
@@ -78,6 +78,9 @@
 #include <rte_version.h>
 #include <rte_atomic.h>
 #include <malloc_heap.h>
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#endif
 
 #include "eal_private.h"
 #include "eal_thread.h"
@@ -478,6 +481,20 @@ eal_parse_vfio_intr(const char *mode)
 	return -1;
 }
 
+#ifdef RTE_LIBRTE_CFGFILE
+/* Parse the arguments for --log-level only */
+static void
+eal_log_level_cfg(struct rte_cfgfile *cfg)
+{
+	const char *entry;
+
+	entry = rte_cfgfile_get_entry(cfg, "DPDK", OPT_LOG_LEVEL);
+	if (entry)
+		eal_parse_common_option(OPT_LOG_LEVEL_NUM, entry,
+				&internal_config);
+}
+#endif
+
 /* Parse the arguments for --log-level only */
 static void
 eal_log_level_parse(int argc, char **argv)
@@ -515,119 +532,135 @@ eal_log_level_parse(int argc, char **argv)
 	optarg = old_optarg;
 }
 
-/* Parse the argument given in the command line of the application */
+/* Parse single argument */
 static int
-eal_parse_args(int argc, char **argv)
+eal_parse_option(int opt, char *optarg, int option_index, char *prgname)
 {
-	int opt, ret;
-	char **argvopt;
-	int option_index;
-	char *prgname = argv[0];
-	const int old_optind = optind;
-	const int old_optopt = optopt;
-	char * const old_optarg = optarg;
+	int ret;
 
-	argvopt = argv;
-	optind = 1;
+	/* getopt is not happy, stop right now */
+	if (opt == '?') {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
 
-	while ((opt = getopt_long(argc, argvopt, eal_short_options,
-				  eal_long_options, &option_index)) != EOF) {
+	ret = eal_parse_common_option(opt, optarg, &internal_config);
+	/* common parser is not happy */
+	if (ret < 0) {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+	/* common parser handled this option */
+	if (ret == 0)
+		return 0;
 
-		/* getopt is not happy, stop right now */
-		if (opt == '?') {
+	switch (opt) {
+	case 'h':
+		eal_usage(prgname);
+		exit(EXIT_SUCCESS);
+		break;
+
+	/* long options */
+	case OPT_XEN_DOM0_NUM:
+#ifdef RTE_LIBRTE_XEN_DOM0
+		internal_config.xen_dom0_support = 1;
+		break;
+#else
+		RTE_LOG(ERR, EAL, "Can't support DPDK app "
+			"running on Dom0, please configure"
+			" RTE_LIBRTE_XEN_DOM0=y\n");
+		ret = -1;
+		goto out;
+#endif
+
+	case OPT_HUGE_DIR_NUM:
+		internal_config.hugepage_dir = optarg;
+		break;
+
+	case OPT_FILE_PREFIX_NUM:
+		internal_config.hugefile_prefix = optarg;
+		break;
+
+	case OPT_SOCKET_MEM_NUM:
+		if (eal_parse_socket_mem(optarg) < 0) {
+			RTE_LOG(ERR, EAL, "invalid parameters for --"
+					OPT_SOCKET_MEM "\n");
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
 		}
+		break;
 
-		ret = eal_parse_common_option(opt, optarg, &internal_config);
-		/* common parser is not happy */
-		if (ret < 0) {
+	case OPT_BASE_VIRTADDR_NUM:
+		if (eal_parse_base_virtaddr(optarg) < 0) {
+			RTE_LOG(ERR, EAL, "invalid parameter for --"
+					OPT_BASE_VIRTADDR "\n");
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
 		}
-		/* common parser handled this option */
-		if (ret == 0)
-			continue;
+		break;
 
-		switch (opt) {
-		case 'h':
+	case OPT_VFIO_INTR_NUM:
+		if (eal_parse_vfio_intr(optarg) < 0) {
+			RTE_LOG(ERR, EAL, "invalid parameters for --"
+					OPT_VFIO_INTR "\n");
 			eal_usage(prgname);
-			exit(EXIT_SUCCESS);
-
-		/* long options */
-		case OPT_XEN_DOM0_NUM:
-#ifdef RTE_LIBRTE_XEN_DOM0
-			internal_config.xen_dom0_support = 1;
-#else
-			RTE_LOG(ERR, EAL, "Can't support DPDK app "
-				"running on Dom0, please configure"
-				" RTE_LIBRTE_XEN_DOM0=y\n");
 			ret = -1;
 			goto out;
-#endif
-			break;
+		}
+		break;
 
-		case OPT_HUGE_DIR_NUM:
-			internal_config.hugepage_dir = optarg;
-			break;
+	case OPT_CREATE_UIO_DEV_NUM:
+		internal_config.create_uio_dev = 1;
+		break;
 
-		case OPT_FILE_PREFIX_NUM:
-			internal_config.hugefile_prefix = optarg;
-			break;
+	default:
+		if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
+			RTE_LOG(ERR, EAL, "Option %c is not supported "
+				"on Linux\n", opt);
+		} else if (opt >= OPT_LONG_MIN_NUM &&
+			   opt < OPT_LONG_MAX_NUM) {
+			RTE_LOG(ERR, EAL, "Option %s is not supported "
+				"on Linux\n",
+				eal_long_options[option_index].name);
+		} else {
+			RTE_LOG(ERR, EAL, "Option %d is not supported "
+				"on Linux\n", opt);
+		}
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
 
-		case OPT_SOCKET_MEM_NUM:
-			if (eal_parse_socket_mem(optarg) < 0) {
-				RTE_LOG(ERR, EAL, "invalid parameters for --"
-						OPT_SOCKET_MEM "\n");
-				eal_usage(prgname);
-				ret = -1;
-				goto out;
-			}
-			break;
+	return 0;
+out:
+	return ret;
+}
 
-		case OPT_BASE_VIRTADDR_NUM:
-			if (eal_parse_base_virtaddr(optarg) < 0) {
-				RTE_LOG(ERR, EAL, "invalid parameter for --"
-						OPT_BASE_VIRTADDR "\n");
-				eal_usage(prgname);
-				ret = -1;
-				goto out;
-			}
-			break;
+/* Parse the argument given in the command line of the application */
+static int
+eal_parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	const int old_optind = optind;
+	const int old_optopt = optopt;
+	char * const old_optarg = optarg;
 
-		case OPT_VFIO_INTR_NUM:
-			if (eal_parse_vfio_intr(optarg) < 0) {
-				RTE_LOG(ERR, EAL, "invalid parameters for --"
-						OPT_VFIO_INTR "\n");
-				eal_usage(prgname);
-				ret = -1;
-				goto out;
-			}
-			break;
+	argvopt = argv;
+	optind = 1;
 
-		case OPT_CREATE_UIO_DEV_NUM:
-			internal_config.create_uio_dev = 1;
-			break;
+	while ((opt = getopt_long(argc, argvopt, eal_short_options,
+				  eal_long_options, &option_index)) != EOF) {
 
-		default:
-			if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
-				RTE_LOG(ERR, EAL, "Option %c is not supported "
-					"on Linux\n", opt);
-			} else if (opt >= OPT_LONG_MIN_NUM &&
-				   opt < OPT_LONG_MAX_NUM) {
-				RTE_LOG(ERR, EAL, "Option %s is not supported "
-					"on Linux\n",
-					eal_long_options[option_index].name);
-			} else {
-				RTE_LOG(ERR, EAL, "Option %d is not supported "
-					"on Linux\n", opt);
-			}
-			eal_usage(prgname);
-			ret = -1;
+		ret = eal_parse_option(opt, optarg, option_index, prgname);
+		if (ret < 0)
 			goto out;
-		}
 	}
 
 	if (eal_adjust_config(&internal_config) != 0) {
@@ -995,3 +1028,149 @@ rte_eal_check_module(const char *module_name)
 	/* Module has been found */
 	return 1;
 }
+
+#ifdef RTE_LIBRTE_CFGFILE
+#define vdev_buff_size		200
+#define sectionname_size	20
+static int
+parse_vdev_devices(struct rte_cfgfile *cfg)
+{
+	char sectionname[sectionname_size];
+	char buffer1[vdev_buff_size];
+	int vdev_nb = 0;
+	int n_entries;
+
+	int i;
+
+	/* ----------- parsing VDEVS */
+	snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
+
+	for (vdev_nb = 1; rte_cfgfile_has_section(cfg, sectionname);
+			vdev_nb++) {
+		n_entries = rte_cfgfile_section_num_entries(cfg, sectionname);
+
+		struct rte_cfgfile_entry entries[n_entries];
+
+
+		if (n_entries != rte_cfgfile_section_entries(cfg, sectionname,
+				entries, n_entries)) {
+			rte_eal_init_alert("Unexpected fault.");
+			rte_errno = EFAULT;
+			return -1;
+		}
+
+		buffer1[0] = 0;
+		for (i = 0; i < n_entries; i++) {
+			if (strlen(entries[i].value)) {
+
+				if ((strlen(buffer1) +
+						strlen(entries[i].name) +
+						strlen(entries[i].value) + 3)
+						>= vdev_buff_size)
+					goto buff_size_err;
+				strcat(buffer1, entries[i].name);
+				strcat(buffer1, "=");
+				strcat(buffer1, entries[i].value);
+			} else {
+				if ((strlen(buffer1) +
+						strlen(entries[i].name) + 2)
+						>= vdev_buff_size)
+					goto buff_size_err;
+				strcat(buffer1, entries[i].name);
+			}
+
+			if (i < (n_entries - 1))
+				strcat(buffer1, ",");
+		}
+
+		/* parsing vdev */
+		if (rte_eal_devargs_add(RTE_DEVTYPE_VIRTUAL,
+				buffer1) < 0) {
+			return -1;
+		}
+		snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
+	}
+	/* ----------- parsing VDEVS */
+	return 0;
+
+buff_size_err:
+	printf("parse_vdev_devices(): buffer size is to small\n");
+	return -1;
+}
+
+static void
+eal_getopt(const char *str, int *opt, int *option_index)
+{
+	int i;
+
+	*opt = '?';
+	*option_index = 0;
+
+	if (strlen(str) == 1) {
+		*opt = *str;
+		return;
+	}
+
+	for (i = 0; eal_long_options[i].name != NULL; i++) {
+		if (strcmp(str, eal_long_options[i].name) == 0) {
+			*opt = eal_long_options[i].val;
+			*option_index = i;
+			break;
+		}
+	}
+}
+
+int
+rte_eal_configure(struct rte_cfgfile *cfg, char *prgname)
+{
+	int n_entries;
+	int i;
+	int opt;
+	int option_index;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, "DPDK");
+
+	if (n_entries < 1) {
+		printf("No DPDK section entries in cfgfile object\n");
+		return 0;
+	}
+
+	struct rte_cfgfile_entry entries[n_entries];
+
+	if (n_entries !=
+			rte_cfgfile_section_entries(cfg, "DPDK", entries,
+					n_entries)) {
+		rte_eal_init_alert("Unexpected fault.");
+		rte_errno = EFAULT;
+		return -1;
+	}
+
+	eal_reset_internal_config(&internal_config);
+
+	/* set log level as early as possible */
+	eal_log_level_cfg(cfg);
+
+	for (i = 0; i < n_entries; i++) {
+		eal_getopt(entries[i].name, &opt, &option_index);
+
+		if (eal_parse_option(opt, entries[i].value,
+				option_index, prgname) != 0) {
+			rte_eal_init_alert("Invalid config file arguments.");
+			rte_errno = EINVAL;
+			return -1;
+		}
+	}
+
+	if (parse_vdev_devices(cfg) < 0) {
+		rte_eal_init_alert("Couldn't parse vdevs");
+		rte_errno = ENOMEM;
+		return -1;
+	}
+	return 0;
+}
+#endif
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 670bab3..c93e6d9 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -198,3 +198,7 @@ DPDK_17.05 {
 	vfio_get_group_no;
 
 } DPDK_17.02;
+
+DPDK_17.08 {
+	rte_eal_configure;
+} DPDK_17.05;
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index bcaf1b3..642af92 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -80,7 +80,6 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_POWER)          += -lrte_power
 
 _LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER)          += -lrte_timer
 _LDLIBS-$(CONFIG_RTE_LIBRTE_EFD)            += -lrte_efd
-_LDLIBS-$(CONFIG_RTE_LIBRTE_CFGFILE)        += -lrte_cfgfile
 
 _LDLIBS-y += --whole-archive
 
@@ -96,6 +95,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL)        += -lrte_mempool
 _LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING)   += -lrte_mempool_ring
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RING)           += -lrte_ring
 _LDLIBS-$(CONFIG_RTE_LIBRTE_EAL)            += -lrte_eal
+_LDLIBS-$(CONFIG_RTE_LIBRTE_CFGFILE)        += -lrte_cfgfile
 _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline
 _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v3 2/3] app/testpmd: changed example to parse options from cfg file
  2017-06-27 10:52       ` [PATCH v3 0/3] EAL change for using a config file for DPDK Jacek Piasecki
  2017-06-27 10:52         ` [PATCH v3 1/3] eal: add functions parsing EAL arguments Jacek Piasecki
@ 2017-06-27 10:52         ` Jacek Piasecki
  2017-06-27 10:52         ` [PATCH v3 3/3] app/testpmd: add parse arguments from JSON config file Jacek Piasecki
  2017-07-05  0:00         ` [PATCH v3 0/3] EAL change for using a config file for DPDK Thomas Monjalon
  3 siblings, 0 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-27 10:52 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Kuba Kozak

From: Kuba Kozak <kubax.kozak@intel.com>

This patch shows how to pass arguments to application
using config.ini file.

If a --cfgfile-path <path> option is passed into commandline
non EAL section, then the file is loaded and used by app.

If a config.ini file is present in current working
directory, and no --cfgfile-path option is passed in, config.ini
file will be loaded and used by app as default configuration.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test-pmd/config.ini   |   24 +
 app/test-pmd/parameters.c | 1193 ++++++++++++++++++++++++++-------------------
 app/test-pmd/testpmd.c    |   67 ++-
 app/test-pmd/testpmd.h    |    3 +-
 4 files changed, 790 insertions(+), 497 deletions(-)
 create mode 100644 app/test-pmd/config.ini

diff --git a/app/test-pmd/config.ini b/app/test-pmd/config.ini
new file mode 100644
index 0000000..54c83a2
--- /dev/null
+++ b/app/test-pmd/config.ini
@@ -0,0 +1,24 @@
+[DPDK]
+v =
+l = 0-4
+n = 4
+master-lcore = 0
+proc-type = primary
+
+[TEST-PMD]
+i =
+portmask = 0xff
+nb-cores = 4
+port-topology = paired
+
+[DPDK.vdev0]
+net_ring0 =
+
+[DPDK.vdev1]
+net_ring1 =
+
+[DPDK.vdev2]
+net_ring2 =
+
+[DPDK.vdev3]
+net_ring3 =
\ No newline at end of file
diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index fbe6284..4202691 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -77,9 +77,96 @@
 #include <rte_eth_bond.h>
 #endif
 #include <rte_flow.h>
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#endif
 
 #include "testpmd.h"
 
+enum { TX, RX };
+
+static struct option lgopts[] = {
+	{ "cfgfile-path",		1, 0, 1 },
+	{ "help",			0, 0, 0 },
+#ifdef RTE_LIBRTE_CMDLINE
+	{ "interactive",		0, 0, 0 },
+	{ "cmdline-file",		1, 0, 0 },
+	{ "auto-start",			0, 0, 0 },
+	{ "eth-peers-configfile",	1, 0, 0 },
+	{ "eth-peer",			1, 0, 0 },
+#endif
+	{ "ports",			1, 0, 0 },
+	{ "nb-cores",			1, 0, 0 },
+	{ "nb-ports",			1, 0, 0 },
+	{ "coremask",			1, 0, 0 },
+	{ "portmask",			1, 0, 0 },
+	{ "numa",			0, 0, 0 },
+	{ "no-numa",			0, 0, 0 },
+	{ "mp-anon",			0, 0, 0 },
+	{ "port-numa-config",           1, 0, 0 },
+	{ "ring-numa-config",           1, 0, 0 },
+	{ "socket-num",			1, 0, 0 },
+	{ "mbuf-size",			1, 0, 0 },
+	{ "total-num-mbufs",		1, 0, 0 },
+	{ "max-pkt-len",		1, 0, 0 },
+	{ "pkt-filter-mode",            1, 0, 0 },
+	{ "pkt-filter-report-hash",     1, 0, 0 },
+	{ "pkt-filter-size",            1, 0, 0 },
+	{ "pkt-filter-drop-queue",      1, 0, 0 },
+#ifdef RTE_LIBRTE_LATENCY_STATS
+	{ "latencystats",               1, 0, 0 },
+#endif
+#ifdef RTE_LIBRTE_BITRATE
+	{ "bitrate-stats",              1, 0, 0 },
+#endif
+	{ "disable-crc-strip",          0, 0, 0 },
+	{ "enable-lro",                 0, 0, 0 },
+	{ "enable-rx-cksum",            0, 0, 0 },
+	{ "enable-scatter",             0, 0, 0 },
+	{ "disable-hw-vlan",            0, 0, 0 },
+	{ "disable-hw-vlan-filter",     0, 0, 0 },
+	{ "disable-hw-vlan-strip",      0, 0, 0 },
+	{ "disable-hw-vlan-extend",     0, 0, 0 },
+	{ "enable-drop-en",            0, 0, 0 },
+	{ "disable-rss",                0, 0, 0 },
+	{ "port-topology",              1, 0, 0 },
+	{ "forward-mode",               1, 0, 0 },
+	{ "rss-ip",			0, 0, 0 },
+	{ "rss-udp",			0, 0, 0 },
+	{ "rxq",			1, 0, 0 },
+	{ "txq",			1, 0, 0 },
+	{ "rxd",			1, 0, 0 },
+	{ "txd",			1, 0, 0 },
+	{ "burst",			1, 0, 0 },
+	{ "mbcache",			1, 0, 0 },
+	{ "txpt",			1, 0, 0 },
+	{ "txht",			1, 0, 0 },
+	{ "txwt",			1, 0, 0 },
+	{ "txfreet",			1, 0, 0 },
+	{ "txrst",			1, 0, 0 },
+	{ "txqflags",			1, 0, 0 },
+	{ "rxpt",			1, 0, 0 },
+	{ "rxht",			1, 0, 0 },
+	{ "rxwt",			1, 0, 0 },
+	{ "rxfreet",                    1, 0, 0 },
+	{ "tx-queue-stats-mapping",	1, 0, 0 },
+	{ "rx-queue-stats-mapping",	1, 0, 0 },
+	{ "no-flush-rx",	0, 0, 0 },
+	{ "txpkts",			1, 0, 0 },
+	{ "disable-link-check",		0, 0, 0 },
+	{ "no-lsc-interrupt",		0, 0, 0 },
+	{ "no-rmv-interrupt",		0, 0, 0 },
+	{ "print-event",		1, 0, 0 },
+	{ "mask-event",			1, 0, 0 },
+	{ 0, 0, 0, 0 },
+};
+
+#ifdef RTE_LIBRTE_CMDLINE
+#define SHORTOPTS "i"
+#else
+#define SHORTOPTS ""
+#endif
+
 static void
 usage(char* progname)
 {
@@ -549,559 +636,675 @@ parse_event_printing_config(const char *optarg, int enable)
 	return 0;
 }
 
-void
-launch_args_parse(int argc, char** argv)
+static int
+parse_option(int opt, char *optarg, int opt_idx, char *prgname)
 {
-	int n, opt;
-	char **argvopt;
-	int opt_idx;
-	enum { TX, RX };
+	int n;
 
-	static struct option lgopts[] = {
-		{ "help",			0, 0, 0 },
+	switch (opt) {
 #ifdef RTE_LIBRTE_CMDLINE
-		{ "interactive",		0, 0, 0 },
-		{ "cmdline-file",		1, 0, 0 },
-		{ "auto-start",			0, 0, 0 },
-		{ "eth-peers-configfile",	1, 0, 0 },
-		{ "eth-peer",			1, 0, 0 },
-#endif
-		{ "ports",			1, 0, 0 },
-		{ "nb-cores",			1, 0, 0 },
-		{ "nb-ports",			1, 0, 0 },
-		{ "coremask",			1, 0, 0 },
-		{ "portmask",			1, 0, 0 },
-		{ "numa",			0, 0, 0 },
-		{ "no-numa",			0, 0, 0 },
-		{ "mp-anon",			0, 0, 0 },
-		{ "port-numa-config",           1, 0, 0 },
-		{ "ring-numa-config",           1, 0, 0 },
-		{ "socket-num",			1, 0, 0 },
-		{ "mbuf-size",			1, 0, 0 },
-		{ "total-num-mbufs",		1, 0, 0 },
-		{ "max-pkt-len",		1, 0, 0 },
-		{ "pkt-filter-mode",            1, 0, 0 },
-		{ "pkt-filter-report-hash",     1, 0, 0 },
-		{ "pkt-filter-size",            1, 0, 0 },
-		{ "pkt-filter-drop-queue",      1, 0, 0 },
-#ifdef RTE_LIBRTE_LATENCY_STATS
-		{ "latencystats",               1, 0, 0 },
-#endif
-#ifdef RTE_LIBRTE_BITRATE
-		{ "bitrate-stats",              1, 0, 0 },
+	case 'i':
+		printf("Interactive-mode selected\n");
+		interactive = 1;
+		break;
 #endif
-		{ "disable-crc-strip",          0, 0, 0 },
-		{ "enable-lro",                 0, 0, 0 },
-		{ "enable-rx-cksum",            0, 0, 0 },
-		{ "enable-scatter",             0, 0, 0 },
-		{ "disable-hw-vlan",            0, 0, 0 },
-		{ "disable-hw-vlan-filter",     0, 0, 0 },
-		{ "disable-hw-vlan-strip",      0, 0, 0 },
-		{ "disable-hw-vlan-extend",     0, 0, 0 },
-		{ "enable-drop-en",            0, 0, 0 },
-		{ "disable-rss",                0, 0, 0 },
-		{ "port-topology",              1, 0, 0 },
-		{ "forward-mode",               1, 0, 0 },
-		{ "rss-ip",			0, 0, 0 },
-		{ "rss-udp",			0, 0, 0 },
-		{ "rxq",			1, 0, 0 },
-		{ "txq",			1, 0, 0 },
-		{ "rxd",			1, 0, 0 },
-		{ "txd",			1, 0, 0 },
-		{ "burst",			1, 0, 0 },
-		{ "mbcache",			1, 0, 0 },
-		{ "txpt",			1, 0, 0 },
-		{ "txht",			1, 0, 0 },
-		{ "txwt",			1, 0, 0 },
-		{ "txfreet",			1, 0, 0 },
-		{ "txrst",			1, 0, 0 },
-		{ "txqflags",			1, 0, 0 },
-		{ "rxpt",			1, 0, 0 },
-		{ "rxht",			1, 0, 0 },
-		{ "rxwt",			1, 0, 0 },
-		{ "rxfreet",                    1, 0, 0 },
-		{ "tx-queue-stats-mapping",	1, 0, 0 },
-		{ "rx-queue-stats-mapping",	1, 0, 0 },
-		{ "no-flush-rx",	0, 0, 0 },
-		{ "txpkts",			1, 0, 0 },
-		{ "disable-link-check",		0, 0, 0 },
-		{ "no-lsc-interrupt",		0, 0, 0 },
-		{ "no-rmv-interrupt",		0, 0, 0 },
-		{ "print-event",		1, 0, 0 },
-		{ "mask-event",			1, 0, 0 },
-		{ 0, 0, 0, 0 },
-	};
+	case 'a':
+		printf("Auto-start selected\n");
+		auto_start = 1;
+		break;
 
-	argvopt = argv;
-
-#ifdef RTE_LIBRTE_CMDLINE
-#define SHORTOPTS "i"
-#else
-#define SHORTOPTS ""
-#endif
-	while ((opt = getopt_long(argc, argvopt, SHORTOPTS "ah",
-				 lgopts, &opt_idx)) != EOF) {
-		switch (opt) {
+	case 0: /*long options */
+		if (!strcmp(lgopts[opt_idx].name, "help")) {
+			usage(prgname);
+			rte_exit(EXIT_SUCCESS, "Displayed help\n");
+			return 0;
+		}
 #ifdef RTE_LIBRTE_CMDLINE
-		case 'i':
+		if (!strcmp(lgopts[opt_idx].name, "interactive")) {
 			printf("Interactive-mode selected\n");
 			interactive = 1;
-			break;
-#endif
-		case 'a':
+		}
+		if (!strcmp(lgopts[opt_idx].name, "cmdline-file")) {
+			printf("CLI commands to be read from %s\n",
+			       optarg);
+			snprintf(cmdline_filename,
+				 sizeof(cmdline_filename), "%s",
+				 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "auto-start")) {
 			printf("Auto-start selected\n");
 			auto_start = 1;
-			break;
+		}
+		if (!strcmp(lgopts[opt_idx].name,
+			    "eth-peers-configfile")) {
+			if (init_peer_eth_addrs(optarg) != 0)
+				rte_exit(EXIT_FAILURE,
+					 "Cannot open logfile\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "eth-peer")) {
+			char *port_end;
+			uint8_t c, peer_addr[6];
 
-		case 0: /*long options */
-			if (!strcmp(lgopts[opt_idx].name, "help")) {
-				usage(argv[0]);
-				rte_exit(EXIT_SUCCESS, "Displayed help\n");
-			}
-#ifdef RTE_LIBRTE_CMDLINE
-			if (!strcmp(lgopts[opt_idx].name, "interactive")) {
-				printf("Interactive-mode selected\n");
-				interactive = 1;
-			}
-			if (!strcmp(lgopts[opt_idx].name, "cmdline-file")) {
-				printf("CLI commands to be read from %s\n",
-				       optarg);
-				snprintf(cmdline_filename,
-					 sizeof(cmdline_filename), "%s",
-					 optarg);
-			}
-			if (!strcmp(lgopts[opt_idx].name, "auto-start")) {
-				printf("Auto-start selected\n");
-				auto_start = 1;
-			}
-			if (!strcmp(lgopts[opt_idx].name,
-				    "eth-peers-configfile")) {
-				if (init_peer_eth_addrs(optarg) != 0)
-					rte_exit(EXIT_FAILURE,
-						 "Cannot open logfile\n");
+			errno = 0;
+			n = strtoul(optarg, &port_end, 10);
+			if (errno != 0 || port_end == optarg ||
+					*port_end++ != ',')
+				rte_exit(EXIT_FAILURE,
+					 "Invalid eth-peer: %s", optarg);
+			if (n >= RTE_MAX_ETHPORTS) {
+				rte_exit(EXIT_FAILURE,
+					 "eth-peer: port %d >= "
+					 "RTE_MAX_ETHPORTS(%d)\n",
+					 n, RTE_MAX_ETHPORTS);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "eth-peer")) {
-				char *port_end;
-				uint8_t c, peer_addr[6];
-
-				errno = 0;
-				n = strtoul(optarg, &port_end, 10);
-				if (errno != 0 || port_end == optarg || *port_end++ != ',')
-					rte_exit(EXIT_FAILURE,
-						 "Invalid eth-peer: %s", optarg);
-				if (n >= RTE_MAX_ETHPORTS)
-					rte_exit(EXIT_FAILURE,
-						 "eth-peer: port %d >= RTE_MAX_ETHPORTS(%d)\n",
-						 n, RTE_MAX_ETHPORTS);
 
-				if (cmdline_parse_etheraddr(NULL, port_end,
-						&peer_addr, sizeof(peer_addr)) < 0)
-					rte_exit(EXIT_FAILURE,
-						 "Invalid ethernet address: %s\n",
-						 port_end);
-				for (c = 0; c < 6; c++)
-					peer_eth_addrs[n].addr_bytes[c] =
-						peer_addr[c];
-				nb_peer_eth_addrs++;
+			if (cmdline_parse_etheraddr(NULL, port_end,
+					&peer_addr, sizeof(peer_addr)) < 0) {
+				rte_exit(EXIT_FAILURE,
+					 "Invalid ethernet address: %s\n",
+					 port_end);
+				return -1;
 			}
+			for (c = 0; c < 6; c++)
+				peer_eth_addrs[n].addr_bytes[c] =
+					peer_addr[c];
+			nb_peer_eth_addrs++;
+		}
 #endif
-			if (!strcmp(lgopts[opt_idx].name, "nb-ports")) {
-				n = atoi(optarg);
-				if (n > 0 && n <= nb_ports)
-					nb_fwd_ports = (uint8_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "Invalid port %d\n", n);
+		if (!strcmp(lgopts[opt_idx].name, "nb-ports")) {
+			n = atoi(optarg);
+			if (n > 0 && n <= nb_ports)
+				nb_fwd_ports = (uint8_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "Invalid port %d\n", n);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "nb-cores")) {
-				n = atoi(optarg);
-				if (n > 0 && n <= nb_lcores)
-					nb_fwd_lcores = (uint8_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "nb-cores should be > 0 and <= %d\n",
-						 nb_lcores);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "nb-cores")) {
+			n = atoi(optarg);
+			if (n > 0 && n <= nb_lcores)
+				nb_fwd_lcores = (uint8_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "nb-cores should be > 0 and <= %d\n",
+					 nb_lcores);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "coremask"))
-				parse_fwd_coremask(optarg);
-			if (!strcmp(lgopts[opt_idx].name, "portmask"))
-				parse_fwd_portmask(optarg);
-			if (!strcmp(lgopts[opt_idx].name, "no-numa"))
-				numa_support = 0;
-			if (!strcmp(lgopts[opt_idx].name, "numa"))
-				numa_support = 1;
-			if (!strcmp(lgopts[opt_idx].name, "mp-anon")) {
-				mp_anon = 1;
+		}
+		if (!strcmp(lgopts[opt_idx].name, "coremask"))
+			parse_fwd_coremask(optarg);
+		if (!strcmp(lgopts[opt_idx].name, "portmask"))
+			parse_fwd_portmask(optarg);
+		if (!strcmp(lgopts[opt_idx].name, "no-numa"))
+			numa_support = 0;
+		if (!strcmp(lgopts[opt_idx].name, "numa"))
+			numa_support = 1;
+		if (!strcmp(lgopts[opt_idx].name, "mp-anon"))
+			mp_anon = 1;
+		if (!strcmp(lgopts[opt_idx].name, "port-numa-config")) {
+			if (parse_portnuma_config(optarg)) {
+				rte_exit(EXIT_FAILURE,
+				   "invalid port-numa configuration\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "port-numa-config")) {
-				if (parse_portnuma_config(optarg))
-					rte_exit(EXIT_FAILURE,
-					   "invalid port-numa configuration\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "ring-numa-config"))
+			if (parse_ringnuma_config(optarg)) {
+				rte_exit(EXIT_FAILURE,
+				   "invalid ring-numa configuration\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "ring-numa-config"))
-				if (parse_ringnuma_config(optarg))
-					rte_exit(EXIT_FAILURE,
-					   "invalid ring-numa configuration\n");
-			if (!strcmp(lgopts[opt_idx].name, "socket-num")) {
-				n = atoi(optarg);
-				if (!new_socket_id((uint8_t)n)) {
-					socket_num = (uint8_t)n;
-				} else {
-					print_invalid_socket_id_error();
-					rte_exit(EXIT_FAILURE,
-						"Invalid socket id");
-				}
+		if (!strcmp(lgopts[opt_idx].name, "socket-num")) {
+			n = atoi(optarg);
+			if (!new_socket_id((uint8_t)n)) {
+				socket_num = (uint8_t)n;
+			} else {
+				print_invalid_socket_id_error();
+				rte_exit(EXIT_FAILURE,
+					"Invalid socket id");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "mbuf-size")) {
-				n = atoi(optarg);
-				if (n > 0 && n <= 0xFFFF)
-					mbuf_data_size = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "mbuf-size should be > 0 and < 65536\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "mbuf-size")) {
+			n = atoi(optarg);
+			if (n > 0 && n <= 0xFFFF)
+				mbuf_data_size = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "mbuf-size should be > 0 and < 65536\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "total-num-mbufs")) {
-				n = atoi(optarg);
-				if (n > 1024)
-					param_total_num_mbufs = (unsigned)n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "total-num-mbufs should be > 1024\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "total-num-mbufs")) {
+			n = atoi(optarg);
+			if (n > 1024)
+				param_total_num_mbufs = (unsigned int)n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "total-num-mbufs should be > 1024\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "max-pkt-len")) {
-				n = atoi(optarg);
-				if (n >= ETHER_MIN_LEN) {
-					rx_mode.max_rx_pkt_len = (uint32_t) n;
-					if (n > ETHER_MAX_LEN)
-					    rx_mode.jumbo_frame = 1;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "Invalid max-pkt-len=%d - should be > %d\n",
-						 n, ETHER_MIN_LEN);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "max-pkt-len")) {
+			n = atoi(optarg);
+			if (n >= ETHER_MIN_LEN) {
+				rx_mode.max_rx_pkt_len = (uint32_t) n;
+				if (n > ETHER_MAX_LEN)
+					rx_mode.jumbo_frame = 1;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "Invalid max-pkt-len=%d - should be > %d\n",
+					 n, ETHER_MIN_LEN);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "pkt-filter-mode")) {
-				if (!strcmp(optarg, "signature"))
-					fdir_conf.mode =
-						RTE_FDIR_MODE_SIGNATURE;
-				else if (!strcmp(optarg, "perfect"))
-					fdir_conf.mode = RTE_FDIR_MODE_PERFECT;
-				else if (!strcmp(optarg, "perfect-mac-vlan"))
-					fdir_conf.mode = RTE_FDIR_MODE_PERFECT_MAC_VLAN;
-				else if (!strcmp(optarg, "perfect-tunnel"))
-					fdir_conf.mode = RTE_FDIR_MODE_PERFECT_TUNNEL;
-				else if (!strcmp(optarg, "none"))
-					fdir_conf.mode = RTE_FDIR_MODE_NONE;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "pkt-mode-invalid %s invalid - must be: "
-						 "none, signature, perfect, perfect-mac-vlan"
-						 " or perfect-tunnel\n",
-						 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "pkt-filter-mode")) {
+			if (!strcmp(optarg, "signature"))
+				fdir_conf.mode =
+					RTE_FDIR_MODE_SIGNATURE;
+			else if (!strcmp(optarg, "perfect"))
+				fdir_conf.mode = RTE_FDIR_MODE_PERFECT;
+			else if (!strcmp(optarg, "perfect-mac-vlan"))
+				fdir_conf.mode = RTE_FDIR_MODE_PERFECT_MAC_VLAN;
+			else if (!strcmp(optarg, "perfect-tunnel"))
+				fdir_conf.mode = RTE_FDIR_MODE_PERFECT_TUNNEL;
+			else if (!strcmp(optarg, "none"))
+				fdir_conf.mode = RTE_FDIR_MODE_NONE;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "pkt-mode-invalid %s invalid - must be: "
+					 "none, signature, perfect, perfect-mac-vlan"
+					 " or perfect-tunnel\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name,
-				    "pkt-filter-report-hash")) {
-				if (!strcmp(optarg, "none"))
-					fdir_conf.status =
-						RTE_FDIR_NO_REPORT_STATUS;
-				else if (!strcmp(optarg, "match"))
-					fdir_conf.status =
-						RTE_FDIR_REPORT_STATUS;
-				else if (!strcmp(optarg, "always"))
-					fdir_conf.status =
-						RTE_FDIR_REPORT_STATUS_ALWAYS;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "pkt-filter-report-hash %s invalid "
-						 "- must be: none or match or always\n",
-						 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name,
+			    "pkt-filter-report-hash")) {
+			if (!strcmp(optarg, "none"))
+				fdir_conf.status =
+					RTE_FDIR_NO_REPORT_STATUS;
+			else if (!strcmp(optarg, "match"))
+				fdir_conf.status =
+					RTE_FDIR_REPORT_STATUS;
+			else if (!strcmp(optarg, "always"))
+				fdir_conf.status =
+					RTE_FDIR_REPORT_STATUS_ALWAYS;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "pkt-filter-report-hash %s invalid "
+					 "- must be: none or match or always\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "pkt-filter-size")) {
-				if (!strcmp(optarg, "64K"))
-					fdir_conf.pballoc =
-						RTE_FDIR_PBALLOC_64K;
-				else if (!strcmp(optarg, "128K"))
-					fdir_conf.pballoc =
-						RTE_FDIR_PBALLOC_128K;
-				else if (!strcmp(optarg, "256K"))
-					fdir_conf.pballoc =
-						RTE_FDIR_PBALLOC_256K;
-				else
-					rte_exit(EXIT_FAILURE, "pkt-filter-size %s invalid -"
-						 " must be: 64K or 128K or 256K\n",
-						 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "pkt-filter-size")) {
+			if (!strcmp(optarg, "64K"))
+				fdir_conf.pballoc =
+					RTE_FDIR_PBALLOC_64K;
+			else if (!strcmp(optarg, "128K"))
+				fdir_conf.pballoc =
+					RTE_FDIR_PBALLOC_128K;
+			else if (!strcmp(optarg, "256K"))
+				fdir_conf.pballoc =
+					RTE_FDIR_PBALLOC_256K;
+			else {
+				rte_exit(EXIT_FAILURE, "pkt-filter-size %s invalid -"
+					 " must be: 64K or 128K or 256K\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name,
-				    "pkt-filter-drop-queue")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					fdir_conf.drop_queue = (uint8_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "drop queue %d invalid - must"
-						 "be >= 0 \n", n);
+		}
+		if (!strcmp(lgopts[opt_idx].name,
+			    "pkt-filter-drop-queue")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				fdir_conf.drop_queue = (uint8_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "drop queue %d invalid - must "
+					 "be >= 0\n", n);
+				return -1;
 			}
+		}
 #ifdef RTE_LIBRTE_LATENCY_STATS
-			if (!strcmp(lgopts[opt_idx].name,
-				    "latencystats")) {
-				n = atoi(optarg);
-				if (n >= 0) {
-					latencystats_lcore_id = (lcoreid_t) n;
-					latencystats_enabled = 1;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "invalid lcore id %d for latencystats"
-						 " must be >= 0\n", n);
+		if (!strcmp(lgopts[opt_idx].name,
+			    "latencystats")) {
+			n = atoi(optarg);
+			if (n >= 0) {
+				latencystats_lcore_id = (lcoreid_t) n;
+				latencystats_enabled = 1;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "invalid lcore id %d for latencystats"
+					 " must be >= 0\n", n);
+				return -1;
 			}
+		}
 #endif
 #ifdef RTE_LIBRTE_BITRATE
-			if (!strcmp(lgopts[opt_idx].name, "bitrate-stats")) {
-				n = atoi(optarg);
-				if (n >= 0) {
-					bitrate_lcore_id = (lcoreid_t) n;
-					bitrate_enabled = 1;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "invalid lcore id %d for bitrate stats"
-						 " must be >= 0\n", n);
+		if (!strcmp(lgopts[opt_idx].name, "bitrate-stats")) {
+			n = atoi(optarg);
+			if (n >= 0) {
+				bitrate_lcore_id = (lcoreid_t) n;
+				bitrate_enabled = 1;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "invalid lcore id %d for bitrate stats"
+					 " must be >= 0\n", n);
+				return -1;
 			}
+		}
 #endif
-			if (!strcmp(lgopts[opt_idx].name, "disable-crc-strip"))
-				rx_mode.hw_strip_crc = 0;
-			if (!strcmp(lgopts[opt_idx].name, "enable-lro"))
-				rx_mode.enable_lro = 1;
-			if (!strcmp(lgopts[opt_idx].name, "enable-scatter"))
-				rx_mode.enable_scatter = 1;
-			if (!strcmp(lgopts[opt_idx].name, "enable-rx-cksum"))
-				rx_mode.hw_ip_checksum = 1;
-
-			if (!strcmp(lgopts[opt_idx].name, "disable-hw-vlan")) {
-				rx_mode.hw_vlan_filter = 0;
-				rx_mode.hw_vlan_strip  = 0;
-				rx_mode.hw_vlan_extend = 0;
-			}
+		if (!strcmp(lgopts[opt_idx].name, "disable-crc-strip"))
+			rx_mode.hw_strip_crc = 0;
+		if (!strcmp(lgopts[opt_idx].name, "enable-lro"))
+			rx_mode.enable_lro = 1;
+		if (!strcmp(lgopts[opt_idx].name, "enable-scatter"))
+			rx_mode.enable_scatter = 1;
+		if (!strcmp(lgopts[opt_idx].name, "enable-rx-cksum"))
+			rx_mode.hw_ip_checksum = 1;
 
-			if (!strcmp(lgopts[opt_idx].name,
-					"disable-hw-vlan-filter"))
-				rx_mode.hw_vlan_filter = 0;
-
-			if (!strcmp(lgopts[opt_idx].name,
-					"disable-hw-vlan-strip"))
-				rx_mode.hw_vlan_strip  = 0;
-
-			if (!strcmp(lgopts[opt_idx].name,
-					"disable-hw-vlan-extend"))
-				rx_mode.hw_vlan_extend = 0;
-
-			if (!strcmp(lgopts[opt_idx].name, "enable-drop-en"))
-				rx_drop_en = 1;
-
-			if (!strcmp(lgopts[opt_idx].name, "disable-rss"))
-				rss_hf = 0;
-			if (!strcmp(lgopts[opt_idx].name, "port-topology")) {
-				if (!strcmp(optarg, "paired"))
-					port_topology = PORT_TOPOLOGY_PAIRED;
-				else if (!strcmp(optarg, "chained"))
-					port_topology = PORT_TOPOLOGY_CHAINED;
-				else if (!strcmp(optarg, "loop"))
-					port_topology = PORT_TOPOLOGY_LOOP;
-				else
-					rte_exit(EXIT_FAILURE, "port-topology %s invalid -"
-						 " must be: paired or chained \n",
-						 optarg);
+		if (!strcmp(lgopts[opt_idx].name, "disable-hw-vlan")) {
+			rx_mode.hw_vlan_filter = 0;
+			rx_mode.hw_vlan_strip  = 0;
+			rx_mode.hw_vlan_extend = 0;
+		}
+
+		if (!strcmp(lgopts[opt_idx].name,
+				"disable-hw-vlan-filter"))
+			rx_mode.hw_vlan_filter = 0;
+
+		if (!strcmp(lgopts[opt_idx].name,
+				"disable-hw-vlan-strip"))
+			rx_mode.hw_vlan_strip  = 0;
+
+		if (!strcmp(lgopts[opt_idx].name,
+				"disable-hw-vlan-extend"))
+			rx_mode.hw_vlan_extend = 0;
+
+		if (!strcmp(lgopts[opt_idx].name, "enable-drop-en"))
+			rx_drop_en = 1;
+
+		if (!strcmp(lgopts[opt_idx].name, "disable-rss"))
+			rss_hf = 0;
+		if (!strcmp(lgopts[opt_idx].name, "port-topology")) {
+			if (!strcmp(optarg, "paired"))
+				port_topology = PORT_TOPOLOGY_PAIRED;
+			else if (!strcmp(optarg, "chained"))
+				port_topology = PORT_TOPOLOGY_CHAINED;
+			else if (!strcmp(optarg, "loop"))
+				port_topology = PORT_TOPOLOGY_LOOP;
+			else {
+				rte_exit(EXIT_FAILURE, "port-topology %s invalid -"
+					 " must be: paired or chained\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "forward-mode"))
-				set_pkt_forwarding_mode(optarg);
-			if (!strcmp(lgopts[opt_idx].name, "rss-ip"))
-				rss_hf = ETH_RSS_IP;
-			if (!strcmp(lgopts[opt_idx].name, "rss-udp"))
-				rss_hf = ETH_RSS_UDP;
-			if (!strcmp(lgopts[opt_idx].name, "rxq")) {
-				n = atoi(optarg);
-				if (n >= 0 && n <= (int) MAX_QUEUE_ID)
-					nb_rxq = (queueid_t) n;
-				else
-					rte_exit(EXIT_FAILURE, "rxq %d invalid - must be"
-						  " >= 0 && <= %d\n", n,
-						  (int) MAX_QUEUE_ID);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "forward-mode"))
+			set_pkt_forwarding_mode(optarg);
+		if (!strcmp(lgopts[opt_idx].name, "rss-ip"))
+			rss_hf = ETH_RSS_IP;
+		if (!strcmp(lgopts[opt_idx].name, "rss-udp"))
+			rss_hf = ETH_RSS_UDP;
+		if (!strcmp(lgopts[opt_idx].name, "rxq")) {
+			n = atoi(optarg);
+			if (n >= 0 && n <= (int) MAX_QUEUE_ID)
+				nb_rxq = (queueid_t) n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxq %d invalid - must be"
+					  " >= 0 && <= %d\n", n,
+					  (int) MAX_QUEUE_ID);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txq")) {
-				n = atoi(optarg);
-				if (n >= 0 && n <= (int) MAX_QUEUE_ID)
-					nb_txq = (queueid_t) n;
-				else
-					rte_exit(EXIT_FAILURE, "txq %d invalid - must be"
-						  " >= 0 && <= %d\n", n,
-						  (int) MAX_QUEUE_ID);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txq")) {
+			n = atoi(optarg);
+			if (n >= 0 && n <= (int) MAX_QUEUE_ID)
+				nb_txq = (queueid_t) n;
+			else {
+				rte_exit(EXIT_FAILURE, "txq %d invalid - must be"
+					  " >= 0 && <= %d\n", n,
+					  (int) MAX_QUEUE_ID);
+				return -1;
 			}
-			if (!nb_rxq && !nb_txq) {
-				rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
-						"be non-zero\n");
+		}
+		if (!nb_rxq && !nb_txq) {
+			rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
+					"be non-zero\n");
+			return -1;
+		}
+		if (!strcmp(lgopts[opt_idx].name, "burst")) {
+			n = atoi(optarg);
+			if ((n >= 1) && (n <= MAX_PKT_BURST))
+				nb_pkt_per_burst = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "burst must >= 1 and <= %d]",
+					 MAX_PKT_BURST);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "burst")) {
-				n = atoi(optarg);
-				if ((n >= 1) && (n <= MAX_PKT_BURST))
-					nb_pkt_per_burst = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "burst must >= 1 and <= %d]",
-						 MAX_PKT_BURST);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "mbcache")) {
+			n = atoi(optarg);
+			if ((n >= 0) &&
+			    (n <= RTE_MEMPOOL_CACHE_MAX_SIZE))
+				mb_mempool_cache = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "mbcache must be >= 0 and <= %d\n",
+					 RTE_MEMPOOL_CACHE_MAX_SIZE);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "mbcache")) {
-				n = atoi(optarg);
-				if ((n >= 0) &&
-				    (n <= RTE_MEMPOOL_CACHE_MAX_SIZE))
-					mb_mempool_cache = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "mbcache must be >= 0 and <= %d\n",
-						 RTE_MEMPOOL_CACHE_MAX_SIZE);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txfreet")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_free_thresh = (int16_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txfreet must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txfreet")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_free_thresh = (int16_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txfreet must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txrst")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_rs_thresh = (int16_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txrst must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txrst")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_rs_thresh = (int16_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txrst must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txqflags")) {
+			char *end = NULL;
+
+			n = strtoul(optarg, &end, 16);
+			if (n >= 0)
+				txq_flags = (int32_t)n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "txqflags must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txqflags")) {
-				char *end = NULL;
-				n = strtoul(optarg, &end, 16);
-				if (n >= 0)
-					txq_flags = (int32_t)n;
-				else
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxd")) {
+			n = atoi(optarg);
+			if (n > 0) {
+				if (rx_free_thresh >= n)
 					rte_exit(EXIT_FAILURE,
-						 "txqflags must be >= 0\n");
+						 "rxd must be > "
+						 "rx_free_thresh(%d)\n",
+						 (int)rx_free_thresh);
+				else
+					nb_rxd = (uint16_t) n;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "rxd(%d) invalid - must be > 0\n",
+					 n);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxd")) {
-				n = atoi(optarg);
-				if (n > 0) {
-					if (rx_free_thresh >= n)
-						rte_exit(EXIT_FAILURE,
-							 "rxd must be > "
-							 "rx_free_thresh(%d)\n",
-							 (int)rx_free_thresh);
-					else
-						nb_rxd = (uint16_t) n;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "rxd(%d) invalid - must be > 0\n",
-						 n);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txd")) {
+			n = atoi(optarg);
+			if (n > 0)
+				nb_txd = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE, "txd must be in > 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txd")) {
-				n = atoi(optarg);
-				if (n > 0)
-					nb_txd = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE, "txd must be in > 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txpt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_pthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txpt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txpt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_pthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txpt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txht")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_hthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txht must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txht")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_hthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txht must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txwt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_wthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txwt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txwt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_wthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txwt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxpt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_pthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxpt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxpt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_pthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxpt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxht")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_hthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxht must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxht")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_hthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxht must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxwt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_wthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxwt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxwt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_wthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxwt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxfreet")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_free_thresh = (int16_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxfreet must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxfreet")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_free_thresh = (int16_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxfreet must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "tx-queue-stats-mapping")) {
+			if (parse_queue_stats_mapping_config(optarg, TX)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid TX queue statistics mapping config entered\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "tx-queue-stats-mapping")) {
-				if (parse_queue_stats_mapping_config(optarg, TX)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid TX queue statistics mapping config entered\n");
-				}
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rx-queue-stats-mapping")) {
+			if (parse_queue_stats_mapping_config(optarg, RX)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid RX queue statistics mapping config entered\n");
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rx-queue-stats-mapping")) {
-				if (parse_queue_stats_mapping_config(optarg, RX)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid RX queue statistics mapping config entered\n");
-				}
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txpkts")) {
+			unsigned int seg_lengths[RTE_MAX_SEGS_PER_PKT];
+			unsigned int nb_segs;
+
+			nb_segs = parse_item_list(optarg, "txpkt segments",
+					RTE_MAX_SEGS_PER_PKT, seg_lengths, 0);
+			if (nb_segs > 0)
+				set_tx_pkt_segments(seg_lengths, nb_segs);
+			else {
+				rte_exit(EXIT_FAILURE, "bad txpkts\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txpkts")) {
-				unsigned seg_lengths[RTE_MAX_SEGS_PER_PKT];
-				unsigned int nb_segs;
-
-				nb_segs = parse_item_list(optarg, "txpkt segments",
-						RTE_MAX_SEGS_PER_PKT, seg_lengths, 0);
-				if (nb_segs > 0)
-					set_tx_pkt_segments(seg_lengths, nb_segs);
-				else
-					rte_exit(EXIT_FAILURE, "bad txpkts\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "no-flush-rx"))
+			no_flush_rx = 1;
+		if (!strcmp(lgopts[opt_idx].name, "disable-link-check"))
+			no_link_check = 1;
+		if (!strcmp(lgopts[opt_idx].name, "no-lsc-interrupt"))
+			lsc_interrupt = 0;
+		if (!strcmp(lgopts[opt_idx].name, "no-rmv-interrupt"))
+			rmv_interrupt = 0;
+		if (!strcmp(lgopts[opt_idx].name, "print-event"))
+			if (parse_event_printing_config(optarg, 1)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid print-event argument\n");
+				return -1;
+			}
+		if (!strcmp(lgopts[opt_idx].name, "mask-event"))
+			if (parse_event_printing_config(optarg, 0)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid mask-event argument\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "no-flush-rx"))
-				no_flush_rx = 1;
-			if (!strcmp(lgopts[opt_idx].name, "disable-link-check"))
-				no_link_check = 1;
-			if (!strcmp(lgopts[opt_idx].name, "no-lsc-interrupt"))
-				lsc_interrupt = 0;
-			if (!strcmp(lgopts[opt_idx].name, "no-rmv-interrupt"))
-				rmv_interrupt = 0;
-			if (!strcmp(lgopts[opt_idx].name, "print-event"))
-				if (parse_event_printing_config(optarg, 1)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid print-event argument\n");
-				}
-			if (!strcmp(lgopts[opt_idx].name, "mask-event"))
-				if (parse_event_printing_config(optarg, 0)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid mask-event argument\n");
-				}
 
-			break;
-		case 'h':
-			usage(argv[0]);
-			rte_exit(EXIT_SUCCESS, "Displayed help\n");
-			break;
-		default:
-			usage(argv[0]);
-			rte_exit(EXIT_FAILURE,
-				 "Command line is incomplete or incorrect\n");
+		break;
+	case 'h':
+		usage(prgname);
+		rte_exit(EXIT_SUCCESS, "Displayed help\n");
+		return -1;
+	case 1:
+		/* does nothing*/
+		break;
+	default:
+		usage(prgname);
+		rte_exit(EXIT_FAILURE,
+			 "Command line is incomplete or incorrect\n");
+		return -1;
+	}
+	return 0;
+}
+
+void
+launch_args_parse(int argc, char **argv)
+{
+	int opt;
+	char **argvopt;
+	int opt_idx;
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, SHORTOPTS "ah",
+				 lgopts, &opt_idx)) != EOF) {
+		parse_option(opt, optarg, opt_idx, argv[0]);
+	}
+}
+
+#ifdef RTE_LIBRTE_CFGFILE_DEBUG
+#define max_nb_sections 100
+static void print_cfg(struct rte_cfgfile *cfg)
+{
+	int i, num_sections, num_entries, j;
+
+	printf("-------------------------\n");
+	printf("SECTIONS:\n\n");
+
+	char *sections[max_nb_sections+1];
+
+	for (i = 0; i < max_nb_sections; i++)
+		sections[i] = malloc(sizeof(sections[0]) * 64);
+	sections[i] = 0;
+
+	num_sections = rte_cfgfile_sections(cfg, sections, max_nb_sections);
+
+	for (i = 0; i < num_sections; i++) {
+		printf("************************\n");
+		printf("%s:\n", sections[i]);
+		num_entries = rte_cfgfile_section_num_entries(cfg, sections[i]);
+
+		struct rte_cfgfile_entry entries[num_entries];
+
+		rte_cfgfile_section_entries(cfg, sections[i],
+				entries, num_entries);
+		for (j = 0; j < num_entries; j++)
+			printf("%s -> %s\n", entries[j].name,
+					entries[j].value);
+	}
+	printf("************************\n\n");
+	printf("-------------------------\n\n");
+
+	i = 0;
+	while (sections[i]) {
+		free(sections[i]);
+		sections[i] = 0;
+		i++;
+	}
+}
+#endif
+
+#ifdef RTE_LIBRTE_CFGFILE
+static void
+non_eal_getopt(const char *str, int *opt, int *option_index)
+{
+	int i;
+
+	*opt = '?';
+	*option_index = 0;
+
+	if (strlen(str) == 1) {
+		*opt = *str;
+		return;
+	}
+
+	for (i = 0; lgopts[i].name != NULL; i++) {
+		if (strcmp(str, lgopts[i].name) == 0) {
+			*opt = lgopts[i].val;
+			*option_index = i;
 			break;
 		}
 	}
 }
+#endif
+
+#ifdef RTE_LIBRTE_CFGFILE
+#define APP_SECTION "TEST-PMD"
+int non_eal_configure(struct rte_cfgfile *cfg, char *prgname)
+{
+	int n_entries;
+	int i;
+	int opt;
+	int option_index;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+#ifdef RTE_LIBRTE_CFGFILE_DEBUG
+	print_cfg(cfg);
+#endif
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, APP_SECTION);
+
+	if (n_entries < 1) {
+		printf("No %s section entries "
+				"in cfgfile object\n", APP_SECTION);
+		return -1;
+	}
+
+	struct rte_cfgfile_entry entries[n_entries];
+
+	if (n_entries !=
+		rte_cfgfile_section_entries(cfg, APP_SECTION, entries,
+				n_entries)) {
+		rte_exit(EXIT_FAILURE, "Unexpected fault");
+		return -1;
+	}
+
+	for (i = 0; i < n_entries; i++) {
+		non_eal_getopt(entries[i].name, &opt, &option_index);
+
+		parse_option(opt, entries[i].value,
+				option_index, prgname);
+	}
+	return 0;
+}
+#endif
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index b29328a..cd69a6b 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -90,6 +90,9 @@
 #ifdef RTE_LIBRTE_LATENCY_STATS
 #include <rte_latencystats.h>
 #endif
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#endif
 
 #include "testpmd.h"
 
@@ -2251,15 +2254,66 @@ signal_handler(int signum)
 	}
 }
 
+#ifdef RTE_LIBRTE_CFGFILE
+/* Load config file path from command line */
+static char *
+cfgfile_load_path(int argc, char **argv)
+{
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		if (!strcmp("--cfgfile-path", argv[i])) {
+			if (argv[i+1] && argv[i+1][0] != '-')
+				return strdup(argv[i+1]);
+		} else if (!strncmp("--cfgfile-path=", argv[i], 15)) {
+			char *ptr = argv[i];
+
+			ptr += 15;
+			if (strlen(ptr))
+				return strdup(ptr);
+		}
+	}
+	return NULL;
+}
+#endif
+
+#define APP_NAME "TEST-PMD"
 int
 main(int argc, char** argv)
 {
 	int  diag;
 	uint8_t port_id;
+#ifdef RTE_LIBRTE_CFGFILE
+	struct rte_cfgfile *cfg = NULL;
+	char *config_file = NULL;
+#endif
 
 	signal(SIGINT, signal_handler);
 	signal(SIGTERM, signal_handler);
 
+#ifdef RTE_LIBRTE_CFGFILE
+	/* load --cfgfile-path argument from argv */
+	config_file = cfgfile_load_path(argc, argv);
+
+	if (config_file) {
+		printf("Info: found cfgfile-path \"%s\"\n", config_file);
+	} else {
+		printf("Info: not found cfgfile-path parameter "
+				"(searching for cfgfile "
+				"in default directory)\n");
+		config_file = strdup("config.ini");
+	}
+
+	cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+
+	if (cfg == NULL) {
+		printf("Info: Valid cfgfile not found\n");
+		cfg = rte_cfgfile_create(CFG_FLAG_EMPTY_VALUES);
+	}
+	diag = rte_eal_configure(cfg, argv[0]);
+	if (diag < 0)
+		rte_panic("Cannot init EAL\n");
+#endif
 	diag = rte_eal_init(argc, argv);
 	if (diag < 0)
 		rte_panic("Cannot init EAL\n");
@@ -2289,6 +2343,18 @@ main(int argc, char** argv)
 	latencystats_enabled = 0;
 #endif
 
+#ifdef RTE_LIBRTE_CFGFILE
+	if (argc > 1) {
+		non_eal_configure(cfg, argv[0]);
+		rte_cfgfile_close(cfg);
+		cfg = 0;
+
+		if (config_file) {
+			free(config_file);
+			config_file = 0;
+		}
+	}
+#endif
 	argc -= diag;
 	argv += diag;
 	if (argc > 1)
@@ -2360,6 +2426,5 @@ main(int argc, char** argv)
 		if (rc < 0)
 			return 1;
 	}
-
 	return 0;
 }
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 364502d..93eebbf 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -499,7 +499,7 @@ port_pci_reg_write(struct rte_port *port, uint32_t reg_off, uint32_t reg_v)
 unsigned int parse_item_list(char* str, const char* item_name,
 			unsigned int max_items,
 			unsigned int *parsed_items, int check_unique_values);
-void launch_args_parse(int argc, char** argv);
+void launch_args_parse(int argc, char **argv);
 void cmdline_read_from_file(const char *filename);
 void prompt(void);
 void prompt_exit(void);
@@ -641,6 +641,7 @@ enum print_warning {
 };
 int port_id_is_invalid(portid_t port_id, enum print_warning warning);
 int new_socket_id(unsigned int socket_id);
+int non_eal_configure(struct rte_cfgfile *cfg, char *prgname);
 
 /*
  * Work-around of a compilation error with ICC on invocations of the
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v3 3/3] app/testpmd: add parse arguments from JSON config file
  2017-06-27 10:52       ` [PATCH v3 0/3] EAL change for using a config file for DPDK Jacek Piasecki
  2017-06-27 10:52         ` [PATCH v3 1/3] eal: add functions parsing EAL arguments Jacek Piasecki
  2017-06-27 10:52         ` [PATCH v3 2/3] app/testpmd: changed example to parse options from " Jacek Piasecki
@ 2017-06-27 10:52         ` Jacek Piasecki
  2017-07-05  0:00         ` [PATCH v3 0/3] EAL change for using a config file for DPDK Thomas Monjalon
  3 siblings, 0 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-06-27 10:52 UTC (permalink / raw)
  To: dev; +Cc: bruce.richardson, deepak.k.jain, Kuba Kozak

From: Kuba Kozak <kubax.kozak@intel.com>

This patch shows usage of Jansson library to parse
application arguments from JSON config file.

https://github.com/akheron/jansson

If a --cfgfile-path <path> option is passed into commandline
non EAL section, then the disired JSON file is loaded and used
by app. In case when JSON doesn't exist an INI file is loaded.
The INI file can be passed also from --cfgfile-path option.

If a file called config.ini is present in current working
directory, and no --cfgfile-path option is passed in, config.ini
file will be loaded and used by app.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test-pmd/Makefile    |  6 ++++
 app/test-pmd/config.json | 33 +++++++++++++++++
 app/test-pmd/testpmd.c   | 94 +++++++++++++++++++++++++++++++++++++++++++++++-
 config/common_base       |  5 +++
 4 files changed, 137 insertions(+), 1 deletion(-)
 create mode 100644 app/test-pmd/config.json

diff --git a/app/test-pmd/Makefile b/app/test-pmd/Makefile
index c36be19..a1c84cc 100644
--- a/app/test-pmd/Makefile
+++ b/app/test-pmd/Makefile
@@ -83,6 +83,12 @@ endif
 
 endif
 
+ifeq ($(CONFIG_RTE_JSON_SUPPORT),y)
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -ljansson
+endif
+endif
+
 CFLAGS_cmdline.o := -D_GNU_SOURCE
 
 include $(RTE_SDK)/mk/rte.app.mk
diff --git a/app/test-pmd/config.json b/app/test-pmd/config.json
new file mode 100644
index 0000000..4589dbc
--- /dev/null
+++ b/app/test-pmd/config.json
@@ -0,0 +1,33 @@
+{
+	"DPDK":
+	{
+		"v": "",
+		"l": "0-4",
+		"n": "4",
+		"master-lcore": "0",
+		"proc-type": "primary"
+	},
+	"TEST-PMD":
+	{
+		"i": "",
+		"portmask": "0xff",
+		"nb-cores": "4",
+		"port-topology": "paired"
+	},
+	"DPDK.vdev0":
+	{
+		"net_ring0": ""
+	},
+	"DPDK.vdev1":
+	{
+		"net_ring1": ""
+	},
+	"DPDK.vdev2":
+	{
+		"net_ring2": ""
+	},
+	"DPDK.vdev3":
+	{
+		"net_ring3": ""
+	}
+}
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index cd69a6b..9ce1bbe 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -46,6 +46,10 @@
 
 #include <stdint.h>
 #include <unistd.h>
+
+#ifdef RTE_JSON_SUPPORT
+#include <jansson.h>
+#endif
 #include <inttypes.h>
 
 #include <rte_common.h>
@@ -2275,6 +2279,87 @@ cfgfile_load_path(int argc, char **argv)
 	}
 	return NULL;
 }
+
+#ifdef RTE_JSON_SUPPORT
+/*
+ * Decoding JSON structure to rte_cfgfile structure.
+ * Returns handler to cfgfile object, NULL if error.
+ */
+static struct
+rte_cfgfile *l3fwd_json_to_cfg(json_t *json, int flags)
+{
+	if (!json) {
+		printf("Error: JSON structure is NULL, nothing to parse\n");
+		return NULL;
+	}
+	/* create an empty instance of cfgfile structure */
+	struct rte_cfgfile *cfgfile = rte_cfgfile_create(flags);
+
+	if (!cfgfile)
+		return NULL;
+
+	const char *section;
+	json_t *entry;
+
+	/* set pointer to first section */
+	void *iter_section = json_object_iter(json);
+
+	while (iter_section) {
+
+		section = json_object_iter_key(iter_section);
+		entry = json_object_iter_value(iter_section);
+
+		/* add parsed section name of current section to cfgfile */
+		rte_cfgfile_add_section(cfgfile, section);
+
+		/* set pointer to first entry */
+		void *iter_entry = json_object_iter(entry);
+
+		while (iter_entry) {
+
+			const char *key;
+			const char *value;
+
+			key = json_object_iter_key(iter_entry);
+			value = json_string_value(
+					json_object_iter_value(iter_entry));
+
+			/* add parsed key and value of current entry */
+			/* to cfgfile */
+			rte_cfgfile_add_entry(cfgfile, section, key, value);
+
+			/* pointer to next entry */
+			iter_entry = json_object_iter_next(entry, iter_entry);
+		}
+		/* pointer to next section */
+		iter_section = json_object_iter_next(json, iter_section);
+	}
+	return cfgfile;
+}
+
+/*
+ * Check presence and load JSON file to rte_cfgfile structure.
+ * Returns handler to cfgfile object, NULL if error.
+ */
+static struct
+rte_cfgfile *l3fwd_load_json_to_cfg(const char *path)
+{
+	struct rte_cfgfile *cfgfile = NULL;
+	/* check if config file exist */
+	if (access(path, F_OK) != -1) {
+		/* parse JSON file */
+		json_error_t error;
+		json_t *json = json_load_file(path, 0, &error);
+
+		if (json)
+			cfgfile = l3fwd_json_to_cfg(json, 0);
+		else
+			fprintf(stderr, "JSON error on line %d: %s\n",
+							error.line, error.text);
+	}
+	return cfgfile;
+}
+#endif
 #endif
 
 #define APP_NAME "TEST-PMD"
@@ -2303,9 +2388,16 @@ main(int argc, char** argv)
 				"in default directory)\n");
 		config_file = strdup("config.ini");
 	}
-
+#ifndef RTE_JSON_SUPPORT
 	cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+#endif
 
+#ifdef RTE_JSON_SUPPORT
+	if (strstr(config_file, ".ini"))
+		cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+	else if (strstr(config_file, ".json"))
+		cfg = l3fwd_load_json_to_cfg(config_file);
+#endif
 	if (cfg == NULL) {
 		printf("Info: Valid cfgfile not found\n");
 		cfg = rte_cfgfile_create(CFG_FLAG_EMPTY_VALUES);
diff --git a/config/common_base b/config/common_base
index c1d0e69..0eed278 100644
--- a/config/common_base
+++ b/config/common_base
@@ -734,3 +734,8 @@ CONFIG_RTE_TEST_PMD_RECORD_BURST_STATS=n
 # Compile the crypto performance application
 #
 CONFIG_RTE_APP_CRYPTO_PERF=y
+
+#
+# Compile JSON support
+#
+CONFIG_RTE_JSON_SUPPORT=n
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [PATCH v3 1/4] cfgfile: remove EAL dependency
  2017-06-27 10:26         ` [PATCH v3 1/4] cfgfile: remove EAL dependency Jacek Piasecki
@ 2017-06-30  9:44           ` Bruce Richardson
  2017-06-30 11:16             ` Bruce Richardson
  0 siblings, 1 reply; 70+ messages in thread
From: Bruce Richardson @ 2017-06-30  9:44 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain

On Tue, Jun 27, 2017 at 12:26:47PM +0200, Jacek Piasecki wrote:
> This patch removes the dependency to EAL in cfgfile library.
> 
> Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
> ---
>  lib/librte_cfgfile/Makefile      |  1 +
>  lib/librte_cfgfile/rte_cfgfile.c | 29 +++++++++++++++++------------
>  2 files changed, 18 insertions(+), 12 deletions(-)
> 
> diff --git a/lib/librte_cfgfile/Makefile b/lib/librte_cfgfile/Makefile
> index 755ef11..0bee43e 100644
> --- a/lib/librte_cfgfile/Makefile
> +++ b/lib/librte_cfgfile/Makefile
> @@ -38,6 +38,7 @@ LIB = librte_cfgfile.a
>  
>  CFLAGS += -O3
>  CFLAGS += $(WERROR_FLAGS)
> +CFLAGS += -I$(SRCDIR)/../librte_eal/common/include
>  
>  EXPORT_MAP := rte_cfgfile_version.map
>  
> diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
> index b54a523..c6ae3e3 100644
> --- a/lib/librte_cfgfile/rte_cfgfile.c
> +++ b/lib/librte_cfgfile/rte_cfgfile.c
> @@ -36,7 +36,6 @@
>  #include <string.h>
>  #include <ctype.h>
>  #include <rte_common.h>
> -#include <rte_string_fns.h>
>  
>  #include "rte_cfgfile.h"
>  
> @@ -258,19 +257,25 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
>  
>  			struct rte_cfgfile_section *sect =
>  				cfg->sections[curr_section];
> -			int n;
> +
>  			char *split[2] = {NULL};
> -			n = rte_strsplit(buffer, sizeof(buffer), split, 2, '=');
> -			if (flags & CFG_FLAG_EMPTY_VALUES) {
> -				if ((n < 1) || (n > 2)) {
> -					printf("Error at line %d - cannot split string, n=%d\n",
> -					       lineno, n);
> -					goto error1;
> -				}
> +			split[0] = buffer;
> +			split[1] = memchr(buffer, '=', len);
> +
> +			/* when delimeter not found */
> +			if (split[1] == NULL) {
> +				printf("Error at line %d - cannot "
> +					"split string\n", lineno);
> +				goto error1;

This check for NULL is not needed, as earlier in the function we find
the following:

	if (buffer[0] != '[' && memchr(buffer, '=', len) == NULL)
		continue;

which means that there must be an "=" in buffer by the time we get to this
line.

>  			} else {

FYI, you don't need an else after a goto. Save indentation where we can!
:-)

/Bruce

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3 2/4] cfgfile: add new functions to API
  2017-06-27 10:26         ` [PATCH v3 2/4] cfgfile: add new functions to API Jacek Piasecki
@ 2017-06-30  9:55           ` Bruce Richardson
  2017-06-30 10:28           ` Bruce Richardson
  1 sibling, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-06-30  9:55 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain

On Tue, Jun 27, 2017 at 12:26:48PM +0200, Jacek Piasecki wrote:
> Extend existing cfgfile library with providing new API functions:
> 
> rte_cfgfile_create() - create new cfgfile object
> rte_cfgfile_add_section() - add new section to existing cfgfile
> object
> rte_cfgfile_add_entry() - add new entry to existing cfgfile
> object in specified section
> rte_cfgfile_set_entry() - update existing entry in cfgfile object
> rte_cfgfile_save() - save existing cfgfile object to INI file
> 
> This modification allows to create a cfgfile on
> runtime and opens up the possibility to have applications
> dynamically build up a proper DPDK configuration, rather than having
> to have a pre-existing one.
> 
> Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
> ---
>  lib/librte_cfgfile/rte_cfgfile.c           | 249 +++++++++++++++++++++++++++--
>  lib/librte_cfgfile/rte_cfgfile.h           |  76 +++++++++
>  lib/librte_cfgfile/rte_cfgfile_version.map |  11 ++
>  3 files changed, 323 insertions(+), 13 deletions(-)
> 
<snip>
> +/**
> + * Add entry to specified section in cfgfile instance.
> + *
> + * @param cfg
> + *   Pointer to the cfgfile structure.
> + * @param sectionname
> + *   Given section name to add an entry.
> + * @param entryname
> + *   Entry name to add.
> + * @entryvalue

missing "param" here. Building the docs throws an error on this.

+/home/bruce/dpdk.org/lib/librte_cfgfile/rte_cfgfile.h:161: warning: Found unknown command `\entryvalue'
+/home/bruce/dpdk.org/lib/librte_cfgfile/rte_cfgfile.h:166: warning: The following parameters of rte_cfgfile_add_entry(struct rte_cfgfile *cfg, const char *sectionname, const char *entryname, const char *entryvalue) are not documented:
+  parameter 'entryvalue'

> + *   Entry value to add.
> + * @return
> + *   0 on success, -EEXIST if entry already exist, -EINVAL if bad argument
> + */
> +int rte_cfgfile_add_entry(struct rte_cfgfile *cfg,
> +		const char *sectionname, const char *entryname,
> +		const char *entryvalue);
> +

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3 2/4] cfgfile: add new functions to API
  2017-06-27 10:26         ` [PATCH v3 2/4] cfgfile: add new functions to API Jacek Piasecki
  2017-06-30  9:55           ` Bruce Richardson
@ 2017-06-30 10:28           ` Bruce Richardson
  1 sibling, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-06-30 10:28 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain

On Tue, Jun 27, 2017 at 12:26:48PM +0200, Jacek Piasecki wrote:
> Extend existing cfgfile library with providing new API functions:
> 
> rte_cfgfile_create() - create new cfgfile object
> rte_cfgfile_add_section() - add new section to existing cfgfile
> object
> rte_cfgfile_add_entry() - add new entry to existing cfgfile
> object in specified section
> rte_cfgfile_set_entry() - update existing entry in cfgfile object
> rte_cfgfile_save() - save existing cfgfile object to INI file
> 
> This modification allows to create a cfgfile on
> runtime and opens up the possibility to have applications
> dynamically build up a proper DPDK configuration, rather than having
> to have a pre-existing one.
> 
> Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>

Some comments inline below on the code.

I also think you might want to split this patch into 2 or 3 to make
review easier. For example:
* Patch to add add_section and add_entry functions
* Patch to add set_entry function
* Patch to add create() and save functions

/Bruce

> ---
>  lib/librte_cfgfile/rte_cfgfile.c           | 249 +++++++++++++++++++++++++++--
>  lib/librte_cfgfile/rte_cfgfile.h           |  76 +++++++++
>  lib/librte_cfgfile/rte_cfgfile_version.map |  11 ++
>  3 files changed, 323 insertions(+), 13 deletions(-)
> 
> diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
> index c6ae3e3..518b6ab 100644
> --- a/lib/librte_cfgfile/rte_cfgfile.c
> +++ b/lib/librte_cfgfile/rte_cfgfile.c
> @@ -35,6 +35,7 @@
>  #include <stdlib.h>
>  #include <string.h>
>  #include <ctype.h>
> +#include <errno.h>
>  #include <rte_common.h>
>  
>  #include "rte_cfgfile.h"
> @@ -42,13 +43,17 @@
>  struct rte_cfgfile_section {
>  	char name[CFG_NAME_LEN];
>  	int num_entries;
> -	struct rte_cfgfile_entry *entries[0];
> +	int free_entries;
> +	int allocated_entries;
> +	struct rte_cfgfile_entry **entries;
>  };

I'm not sure that we need to use an array of pointers here any more. It
might work easier to just store the entries for each section in a single
block, i.e. have the last member just:
	"struct rte_cfgfile_entry *entries;"

Also, allocated_entries value seems unneeded, since it is always equal
to num_entries + free_entries. The place where is seems to be used, when
allocating new entries, free_entries == 0, so allocated_entries ==
num_entries, making allocated entries doubly-unneeded there. Another,
possibly better, alternative might be to remove the "free_entries"
variable. [It's possibly better as you don't need to do an increment and
a decrement on adding an entry, but just do an increment]

>  
>  struct rte_cfgfile {
>  	int flags;
>  	int num_sections;
> -	struct rte_cfgfile_section *sections[0];
> +	int free_sections;
> +	int allocated_sections;
> +	struct rte_cfgfile_section **sections;
>  };

As above: for the entries in a section, we should be ok putting all
sections in a single array, rather than having an array of pointers, and
we don't need both an allocated_sections variable and a free_sections
one.

>  
>  /** when we resize a file structure, how many extra entries
> @@ -104,6 +109,65 @@ _strip(char *str, unsigned len)
>  	return newlen;
>  }
>  
> +static struct rte_cfgfile_section *
> +_get_section(struct rte_cfgfile *cfg, const char *sectionname)
> +{
> +	int i;
> +
> +	for (i = 0; i < cfg->num_sections; i++) {
> +		if (strncmp(cfg->sections[i]->name, sectionname,
> +				sizeof(cfg->sections[0]->name)) == 0)
> +			return cfg->sections[i];
> +	}
> +	return NULL;
> +}
> +
> +static int
> +_add_entry(struct rte_cfgfile_section *section, const char *entryname,
> +		const char *entryvalue)
> +{
> +	int i;
> +
> +	/* resize entry structure if we don't have room for more entries */
> +	if (section->free_entries == 0) {
> +
> +		struct rte_cfgfile_entry **n_entries =
> +				realloc(section->entries,
> +				sizeof(section->entries[0]) *
> +				((section->allocated_entries) +
> +						CFG_ALLOC_ENTRY_BATCH));
> +
> +		if (n_entries == NULL)
> +			return -ENOMEM;
> +
> +		section->entries = n_entries;
> +
> +		for (i = section->allocated_entries;
> +				i < (section->allocated_entries) +
> +						CFG_ALLOC_ENTRY_BATCH; i++) {
> +			section->entries[i] =
> +				malloc(sizeof(struct rte_cfgfile_entry));
> +
> +			if (section->entries[i] == NULL)
> +				return -ENOMEM;
> +		}

Even if you keep the entries array as an array of pointers, you still
don't need to loop here for malloc. Instead just malloc a single block
for CFG_ALLOC_ENTRY_BATCH entries in one go, and set the pointers to
point to values in that block. Too many malloc calls is fragmenting
memory.

> +		section->allocated_entries += CFG_ALLOC_ENTRY_BATCH;
> +		section->free_entries += CFG_ALLOC_ENTRY_BATCH;
> +	}
> +
> +	/* fill up entry fields with key name and value */
> +	struct rte_cfgfile_entry *curr_entry =
> +			section->entries[section->num_entries];
> +
> +	snprintf(curr_entry->name, sizeof(curr_entry->name), "%s", entryname);
> +	snprintf(curr_entry->value, sizeof(curr_entry->value), "%s",
> +								entryvalue);
> +	section->num_entries++;
> +	section->free_entries--;
> +
> +	return 0;
> +}
> +
>  static int
>  rte_cfgfile_check_params(const struct rte_cfgfile_parameters *params)
>  {
> @@ -332,6 +396,176 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
>  	return NULL;
>  }
>  
> +struct rte_cfgfile *
> +rte_cfgfile_create(int flags)
> +{
> +	int i, j;
> +	struct rte_cfgfile *cfg = NULL;
> +
> +	cfg = malloc(sizeof(*cfg));
> +	if (cfg == NULL)
> +		return NULL;
> +
> +	memset(cfg, 0, sizeof((*cfg)));
> +
> +	cfg->flags = flags;
> +
> +	/* allocate first batch of sections and entries */
> +	cfg->sections = malloc(sizeof(cfg->sections[0]) *
> +						CFG_ALLOC_SECTION_BATCH);
> +	if (cfg->sections == NULL)
> +		return NULL;
> +
> +	for (i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) {
> +		cfg->sections[i] = malloc(sizeof(struct rte_cfgfile_section));
> +		if (cfg->sections[i] == NULL)
> +			return NULL;
> +
> +		memset(cfg->sections[i], 0,
> +					sizeof(struct rte_cfgfile_section));
> +
> +		cfg->sections[i]->entries =
> +				malloc(sizeof(cfg->sections[i]->entries[0])
> +						* CFG_ALLOC_ENTRY_BATCH);
> +		if (cfg->sections[i]->entries == NULL)
> +			return NULL;
> +
> +		for (j = 0; j < CFG_ALLOC_ENTRY_BATCH; j++) {
> +			cfg->sections[i]->entries[j] = malloc(sizeof(struct
> +							rte_cfgfile_entry));
> +			if (cfg->sections[i]->entries[j] == NULL)
> +				return NULL;
> +		}
> +		cfg->sections[i]->allocated_entries = CFG_ALLOC_ENTRY_BATCH;
> +		cfg->sections[i]->free_entries = CFG_ALLOC_ENTRY_BATCH;
> +	}
> +	cfg->allocated_sections = CFG_ALLOC_SECTION_BATCH;
> +	cfg->free_sections = CFG_ALLOC_SECTION_BATCH;

This work to initialise things is duplicated from the other add_section
and add_entries functions. I'd just remove it from here, and have the
newly allocated cfgfile structure be completely empty. Then on adding
the first section and first entry, the allocation happens. No need to do
it here.

> +
> +	if (flags & CFG_FLAG_GLOBAL_SECTION)
> +		rte_cfgfile_add_section(cfg, "GLOBAL");
> +	return cfg;
> +}
> +
> +int
> +rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname)
> +{
> +	int i;

You are missing parameter checking.

> +	/* resize overall struct if we don't have room for more	sections */
> +	if (cfg->free_sections == 0) {
> +
> +		struct rte_cfgfile_section **n_sections =
> +				realloc(cfg->sections,
> +				sizeof(cfg->sections[0]) *
> +				((cfg->allocated_sections) +
> +						CFG_ALLOC_SECTION_BATCH));
> +
> +		if (n_sections == NULL)
> +			return -ENOMEM;
> +
> +		cfg->sections = n_sections;
> +
> +		for (i = cfg->allocated_sections;
> +				i < (cfg->allocated_sections) +
> +						CFG_ALLOC_SECTION_BATCH; i++) {
> +			cfg->sections[i] =
> +				malloc(sizeof(struct rte_cfgfile_section));
> +
> +			if (cfg->sections[i] == NULL)
> +				return -ENOMEM;
> +
> +			memset(cfg->sections[i], 0,
> +					sizeof(struct rte_cfgfile_section));
> +		}

Same comment for too many mallocs (and memsets) as with add_entry.

> +		cfg->allocated_sections += CFG_ALLOC_SECTION_BATCH;
> +		cfg->free_sections += CFG_ALLOC_SECTION_BATCH;
> +	}
> +
> +	snprintf(cfg->sections[cfg->num_sections]->name,
> +			sizeof(cfg->sections[0]->name), "%s", sectionname);
> +	cfg->sections[cfg->num_sections]->num_entries = 0;
> +
> +	cfg->num_sections++;
> +	cfg->free_sections--;
> +
> +	return 0;
> +}
> +
> +int rte_cfgfile_add_entry(struct rte_cfgfile *cfg,
> +		const char *sectionname, const char *entryname,
> +		const char *entryvalue)
> +{
> +	int ret;
> +
> +	if (cfg == NULL)
> +		return -EINVAL;
> +
Check other parameters for NULL too?

> +	if (rte_cfgfile_has_entry(cfg, sectionname, entryname) != 0)
> +		return -EEXIST;
> +
> +	/* search for section pointer by sectionname */
> +	struct rte_cfgfile_section *curr_section = _get_section(cfg,
> +								sectionname);
> +	if (curr_section == NULL)
> +		return -EINVAL;
> +
> +	ret = _add_entry(curr_section, entryname, entryvalue);
> +
> +	return ret;
> +}
> +
> +int rte_cfgfile_set_entry(struct rte_cfgfile *cfg, const char *sectionname,
> +		const char *entryname, const char *entryvalue)
> +{
> +	int i;
> +
> +	if (cfg == NULL)
> +		return -EINVAL;
> +

parameter checking?

> +	/* search for section pointer by sectionname */
> +	struct rte_cfgfile_section *curr_section = _get_section(cfg,
> +								sectionname);
> +	if (curr_section == NULL)
> +		return -EINVAL;
> +
> +	if (entryvalue == NULL)
> +		entryvalue = "";
> +
> +	for (i = 0; i < curr_section->num_entries; i++)
> +		if (!strcmp(curr_section->entries[i]->name, entryname)) {
> +			strcpy(curr_section->entries[i]->value, entryvalue);

strcpy is unsafe here. Use snprintf as in other cases above where you
are setting string values.

> +			return 0;
> +		}
> +	return -1;
> +}
> +
> +int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename)
> +{
> +	char buffer[CFG_NAME_LEN + CFG_VALUE_LEN + 4] = {0};
> +	int i, j;
> +
> +	if ((cfg == NULL) || (filename == NULL))
> +		return -EINVAL;
> +
> +	FILE *f = fopen(filename, "w");
> +
> +	if (f == NULL)
> +		return -EINVAL;

Other functions e.g. the one above return -1 on error (and should set
rte_errno appropriately in that case). Please standardize on one
approach for error handling in this library - either return -1 and set
rte_errno (my preference), or return error code directly. Mixing both is
confusing.

> +
> +	for (i = 0; i < cfg->num_sections; i++) {
> +		snprintf(buffer, sizeof(buffer), "[%s]\n",
> +					cfg->sections[i]->name);
> +		fputs(buffer, f);
> +
> +		for (j = 0; j < cfg->sections[i]->num_entries; j++) {
> +			snprintf(buffer, sizeof(buffer), "%s=%s\n",
> +				cfg->sections[i]->entries[j]->name,
> +				cfg->sections[i]->entries[j]->value);
> +			fputs(buffer, f);

Is there a reason to write to the buffer first and then to the file? Why
not just write directly to the file?
Check the return value from the write too.

> +		}
> +	}
> +	return fclose(f);
> +}
>  
>  int rte_cfgfile_close(struct rte_cfgfile *cfg)
>  {
> @@ -385,17 +619,6 @@ rte_cfgfile_sections(struct rte_cfgfile *cfg, char *sections[],
>  	return i;
>  }
>  
> -static const struct rte_cfgfile_section *
> -_get_section(struct rte_cfgfile *cfg, const char *sectionname)
> -{
> -	int i;
> -	for (i = 0; i < cfg->num_sections; i++) {
> -		if (strncmp(cfg->sections[i]->name, sectionname,
> -				sizeof(cfg->sections[0]->name)) == 0)
> -			return cfg->sections[i];
> -	}
> -	return NULL;
> -}
>  
>  int
>  rte_cfgfile_has_section(struct rte_cfgfile *cfg, const char *sectionname)
> diff --git a/lib/librte_cfgfile/rte_cfgfile.h b/lib/librte_cfgfile/rte_cfgfile.h
> index fa10d40..6245c7e 100644
> --- a/lib/librte_cfgfile/rte_cfgfile.h
> +++ b/lib/librte_cfgfile/rte_cfgfile.h
> @@ -121,6 +121,82 @@ struct rte_cfgfile *rte_cfgfile_load_with_params(const char *filename,
>  	int flags, const struct rte_cfgfile_parameters *params);
>  
>  /**
> + * Create new cfgfile instance with empty sections and entries
> + *
> + * @param flags
> + *   - CFG_FLAG_GLOBAL_SECTION
> + *     Indicates that the file supports key value entries before the first
> + *     defined section.  These entries can be accessed in the "GLOBAL"
> + *     section.
> + *   - CFG_FLAG_EMPTY_VALUES
> + *     Indicates that file supports key value entries where the value can
> + *     be zero length (e.g., "key=").
> + * @return
> + *   Handle to cfgfile instance on success, NULL otherwise
> + */
> +struct rte_cfgfile *rte_cfgfile_create(int flags);
> +
> +/**
> + * Add section in cfgfile instance.
> + *
> + * @param cfg
> + *   Pointer to the cfgfile structure.
> + * @param sectionname
> + *   Section name which will be add to cfgfile.
> + * @return
> + *   0 on success, -ENOMEM if can't add section
> + */
> +int
> +rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname);
> +
> +/**
> + * Add entry to specified section in cfgfile instance.
> + *
> + * @param cfg
> + *   Pointer to the cfgfile structure.
> + * @param sectionname
> + *   Given section name to add an entry.
> + * @param entryname
> + *   Entry name to add.
> + * @entryvalue
> + *   Entry value to add.
> + * @return
> + *   0 on success, -EEXIST if entry already exist, -EINVAL if bad argument
> + */
> +int rte_cfgfile_add_entry(struct rte_cfgfile *cfg,
> +		const char *sectionname, const char *entryname,
> +		const char *entryvalue);
> +
> +/**
> + * Update value of specified entry name in given section in config file
> + *
> + * @param cfg
> + *   Config file
> + * @param sectionname
> + *   Section name
> + * @param entryname
> + *   Entry name to look for the value change
> + * @param entryvalue
> + *   New entry value. Can be also an empty string if CFG_FLAG_EMPTY_VALUES = 1
> + * @return
> + *   0 on success, -EINVAL if bad argument

What about if the value doesn't exist?

> + */
> +int rte_cfgfile_set_entry(struct rte_cfgfile *cfg, const char *sectionname,
> +		const char *entryname, const char *entryvalue);
> +
> +/**
> + * Save object cfgfile to file on disc
> + *
> + * @param cfg
> + *   Config file structure
> + * @param filename
> + *   File name to save data
> + * @return
> + *   0 on success, errno otherwise
> + */
> +int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename);
> +
> +/**
>  * Get number of sections in config file
>  *
>  * @param cfg
> diff --git a/lib/librte_cfgfile/rte_cfgfile_version.map b/lib/librte_cfgfile/rte_cfgfile_version.map
> index 5fe60f7..de68ff6 100644
> --- a/lib/librte_cfgfile/rte_cfgfile_version.map
> +++ b/lib/librte_cfgfile/rte_cfgfile_version.map
> @@ -27,3 +27,14 @@ DPDK_17.05 {
>      rte_cfgfile_load_with_params;
>  
>  } DPDK_16.04;
> +
> +DPDK_17.08 {
> +	global:
> +
> +	rte_cfgfile_add_entry;
> +	rte_cfgfile_add_section;
> +	rte_cfgfile_create;
> +	rte_cfgfile_save;
> +	rte_cfgfile_set_entry;
> +
> +} DPDK_17.05;
> \ No newline at end of file
> -- 
> 2.7.4
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3 1/4] cfgfile: remove EAL dependency
  2017-06-30  9:44           ` Bruce Richardson
@ 2017-06-30 11:16             ` Bruce Richardson
  0 siblings, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-06-30 11:16 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain

On Fri, Jun 30, 2017 at 10:44:34AM +0100, Bruce Richardson wrote:
> On Tue, Jun 27, 2017 at 12:26:47PM +0200, Jacek Piasecki wrote:
> > This patch removes the dependency to EAL in cfgfile library.
> > 
> > Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
> > ---
> >  lib/librte_cfgfile/Makefile      |  1 +
> >  lib/librte_cfgfile/rte_cfgfile.c | 29 +++++++++++++++++------------
> >  2 files changed, 18 insertions(+), 12 deletions(-)
> > 
> > diff --git a/lib/librte_cfgfile/Makefile b/lib/librte_cfgfile/Makefile
> > index 755ef11..0bee43e 100644
> > --- a/lib/librte_cfgfile/Makefile
> > +++ b/lib/librte_cfgfile/Makefile
> > @@ -38,6 +38,7 @@ LIB = librte_cfgfile.a
> >  
> >  CFLAGS += -O3
> >  CFLAGS += $(WERROR_FLAGS)
> > +CFLAGS += -I$(SRCDIR)/../librte_eal/common/include
> >  
> >  EXPORT_MAP := rte_cfgfile_version.map
> >  
> > diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
> > index b54a523..c6ae3e3 100644
> > --- a/lib/librte_cfgfile/rte_cfgfile.c
> > +++ b/lib/librte_cfgfile/rte_cfgfile.c
> > @@ -36,7 +36,6 @@
> >  #include <string.h>
> >  #include <ctype.h>
> >  #include <rte_common.h>
> > -#include <rte_string_fns.h>
> >  
> >  #include "rte_cfgfile.h"
> >  
> > @@ -258,19 +257,25 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
> >  
> >  			struct rte_cfgfile_section *sect =
> >  				cfg->sections[curr_section];
> > -			int n;
> > +
> >  			char *split[2] = {NULL};
> > -			n = rte_strsplit(buffer, sizeof(buffer), split, 2, '=');
> > -			if (flags & CFG_FLAG_EMPTY_VALUES) {
> > -				if ((n < 1) || (n > 2)) {
> > -					printf("Error at line %d - cannot split string, n=%d\n",
> > -					       lineno, n);
> > -					goto error1;
> > -				}
> > +			split[0] = buffer;
> > +			split[1] = memchr(buffer, '=', len);
> > +
> > +			/* when delimeter not found */
> > +			if (split[1] == NULL) {
> > +				printf("Error at line %d - cannot "
> > +					"split string\n", lineno);
> > +				goto error1;
> 
> This check for NULL is not needed, as earlier in the function we find
> the following:
> 
> 	if (buffer[0] != '[' && memchr(buffer, '=', len) == NULL)
> 		continue;
> 
> which means that there must be an "=" in buffer by the time we get to this
> line.
> 
> >  			} else {
> 
> FYI, you don't need an else after a goto. Save indentation where we can!
> :-)
> 
I see now that a later patch removes this unneeded "if", but it might be
better to move the removal back to this patch instead.

/Bruce

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3 3/4] cfgfile: rework of load function
  2017-06-27 10:26         ` [PATCH v3 3/4] cfgfile: rework of load function Jacek Piasecki
@ 2017-06-30 11:18           ` Bruce Richardson
  0 siblings, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-06-30 11:18 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain, Kuba Kozak

On Tue, Jun 27, 2017 at 12:26:49PM +0200, Jacek Piasecki wrote:
> From: Kuba Kozak <kubax.kozak@intel.com>
> 
> New functions added to cfgfile library make it possible
> to significantly simplify the code of rte_cfgfile_load_with_params()
> 
> This patch shows the new body of this function.
> 
> Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
> ---
>  lib/librte_cfgfile/rte_cfgfile.c | 143 +++++----------------------------------
>  1 file changed, 17 insertions(+), 126 deletions(-)
> 
> diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
> index 518b6ab..5625c80 100644
> --- a/lib/librte_cfgfile/rte_cfgfile.c
> +++ b/lib/librte_cfgfile/rte_cfgfile.c
> @@ -207,10 +207,6 @@ struct rte_cfgfile *
>  rte_cfgfile_load_with_params(const char *filename, int flags,
>  			     const struct rte_cfgfile_parameters *params)
>  {
> -	int allocated_sections = CFG_ALLOC_SECTION_BATCH;
> -	int allocated_entries = 0;
> -	int curr_section = -1;
> -	int curr_entry = -1;
>  	char buffer[CFG_NAME_LEN + CFG_VALUE_LEN + 4] = {0};
>  	int lineno = 0;
>  	struct rte_cfgfile *cfg = NULL;
> @@ -222,28 +218,7 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
>  	if (f == NULL)
>  		return NULL;
>  
> -	cfg = malloc(sizeof(*cfg) + sizeof(cfg->sections[0]) *
> -		allocated_sections);
> -	if (cfg == NULL)
> -		goto error2;
> -
> -	memset(cfg->sections, 0, sizeof(cfg->sections[0]) * allocated_sections);
> -
> -	if (flags & CFG_FLAG_GLOBAL_SECTION) {
> -		curr_section = 0;
> -		allocated_entries = CFG_ALLOC_ENTRY_BATCH;
> -		cfg->sections[curr_section] = malloc(
> -			sizeof(*cfg->sections[0]) +
> -			sizeof(cfg->sections[0]->entries[0]) *
> -			allocated_entries);
> -		if (cfg->sections[curr_section] == NULL) {
> -			printf("Error - no memory for global section\n");
> -			goto error1;
> -		}
> -
> -		snprintf(cfg->sections[curr_section]->name,
> -				 sizeof(cfg->sections[0]->name), "GLOBAL");
> -	}
> +	cfg = rte_cfgfile_create(flags);
>  
>  	while (fgets(buffer, sizeof(buffer), f) != NULL) {
>  		char *pos = NULL;
> @@ -254,6 +229,7 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
>  					"Check if line too long\n", lineno);
>  			goto error1;
>  		}
> +		/* skip parsing if comment character found */
>  		pos = memchr(buffer, params->comment_character, len);
>  		if (pos != NULL) {
>  			*pos = '\0';
> @@ -261,6 +237,7 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
>  		}
>  
>  		len = _strip(buffer, len);
> +		/* skip lines without useful content */
>  		if (buffer[0] != '[' && memchr(buffer, '=', len) == NULL)
>  			continue;
>  
> @@ -268,130 +245,44 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
>  			/* section heading line */
>  			char *end = memchr(buffer, ']', len);
>  			if (end == NULL) {
> -				printf("Error line %d - no terminating '['"
> +				printf("Error line %d - no terminating ']'"
>  					"character found\n", lineno);
>  				goto error1;
>  			}
>  			*end = '\0';
>  			_strip(&buffer[1], end - &buffer[1]);
>  
> -			/* close off old section and add start new one */
> -			if (curr_section >= 0)
> -				cfg->sections[curr_section]->num_entries =
> -					curr_entry + 1;
> -			curr_section++;
> -
> -			/* resize overall struct if we don't have room for more
> -			sections */
> -			if (curr_section == allocated_sections) {
> -				allocated_sections += CFG_ALLOC_SECTION_BATCH;
> -				struct rte_cfgfile *n_cfg = realloc(cfg,
> -					sizeof(*cfg) + sizeof(cfg->sections[0])
> -					* allocated_sections);
> -				if (n_cfg == NULL) {
> -					curr_section--;
> -					printf("Error - no more memory\n");
> -					goto error1;
> -				}
> -				cfg = n_cfg;
> -			}
> -
> -			/* allocate space for new section */
> -			allocated_entries = CFG_ALLOC_ENTRY_BATCH;
> -			curr_entry = -1;
> -			cfg->sections[curr_section] = malloc(
> -				sizeof(*cfg->sections[0]) +
> -				sizeof(cfg->sections[0]->entries[0]) *
> -				allocated_entries);
> -			if (cfg->sections[curr_section] == NULL) {
> -				printf("Error - no more memory\n");
> -				goto error1;
> -			}
> -
> -			snprintf(cfg->sections[curr_section]->name,
> -					sizeof(cfg->sections[0]->name),
> -					"%s", &buffer[1]);
> +			rte_cfgfile_add_section(cfg, &buffer[1]);
>  		} else {
> -			/* value line */
> -			if (curr_section < 0) {
> -				printf("Error line %d - value outside of"
> -					"section\n", lineno);
> -				goto error1;
> -			}
> -
> -			struct rte_cfgfile_section *sect =
> -				cfg->sections[curr_section];
> -
> +			/* key and value line */
>  			char *split[2] = {NULL};
> +
>  			split[0] = buffer;
>  			split[1] = memchr(buffer, '=', len);
> +			*split[1] = '\0';
> +			split[1]++;
> +
> +			_strip(split[0], strlen(split[0]));
> +			_strip(split[1], strlen(split[1]));
>  
> -			/* when delimeter not found */
> -			if (split[1] == NULL) {
> +			if (!(flags & CFG_FLAG_EMPTY_VALUES) &&
> +					(*split[1] == '\0')) {
>  				printf("Error at line %d - cannot "
>  					"split string\n", lineno);

This error message could do with an update. It's not that you can't
split the string, it's that the value is empty - something not allowed.
Also, it's bad practice to split literal strings for printing. This is
the one case where you are encouraged to go over the 80-char limit
rather than wrapping the line.

/Bruce

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3 0/4] Rework cfgfile API to enable apps config file support
  2017-06-27 10:26       ` [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Jacek Piasecki
                           ` (3 preceding siblings ...)
  2017-06-27 10:26         ` [PATCH v3 4/4] test/cfgfile: add new unit test Jacek Piasecki
@ 2017-06-30 11:20         ` Bruce Richardson
  4 siblings, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-06-30 11:20 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain

On Tue, Jun 27, 2017 at 12:26:46PM +0200, Jacek Piasecki wrote:
> New API for cfgfile library allows to create a cfgfile at runtime, add new
> section, add entry in a section, update existing entry and save cfgfile
> structure to INI file - opens up the possibility to have applications
> dynamically build up a proper DPDK configuration, rather than
> having to have a pre-existing one. Due the new API functions, simplification
> of load() function was made. One new unit test to TEST app was added. It
> contains an example of a large INI file whose parsing requires multiple
> reallocation of memory.
> 
> ---
> v3: 
> 	split one patchset into two distinct patchsets:
> 	1. cfgfile library and TEST app changes
> 	2. EAL changes and examples (this patchset depends on cfgfile)
> v2:
>   lib eal:
>   	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
> 	Now this function load data from cfg structure and did initial
> 	initialization of EAL arguments. Vdev argument are stored in different
> 	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
> 	function it is necessary to call rte_eal_init to complete EAL
> 	initialization. There is no more merging arguments from different
> 	sources (cfg file and command line).
>   	Added non_eal_configure to testpmd application.
> 	Function maintain the same functionality as rte_eal_configure but
> 	for non-eal arguments. 
>   	Added config JSON feature to testpmd last patch from patchset contain
> 	example showing use of .json configuration files.
> 
>   lib cfgfile:
>   	Rework of add_section(), add_entry() new implementation
>   	New members allocated_entries/sections, free_entries/sections
> 	in rte_cfgfile structure, change in array of pointers
> 	**sections, **entries instead of *sections[], *entries[]
>   	Add  set_entry() to update/overwrite already existing entry in cfgfile
> 	struct
>   	Add save() function to save on disc cfgfile structure in INI format
>   	Rework of existing load() function  simplifying the code
>   	Add unit test realloc_sections() in TEST app for testing realloc/malloc
> 	of new API functions, add test for save() function
> 
> Jacek Piasecki (3):
>   cfgfile: remove EAL dependency
>   cfgfile: add new functions to API
>   test/cfgfile: add new unit test
> 
> Kuba Kozak (1):
>   cfgfile: rework of load function
> 

This looks a good set to have. Some cleanup work needed on the code, but
from a high-level implementation view, it's fine. 

Once issues flagged by me are fixed:
Acked-by: Bruce Richardson <bruce.richardson@intel.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3 1/3] eal: add functions parsing EAL arguments
  2017-06-27 10:52         ` [PATCH v3 1/3] eal: add functions parsing EAL arguments Jacek Piasecki
@ 2017-06-30 16:04           ` Bruce Richardson
  2017-07-10 12:44           ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Jacek Piasecki
  2017-07-10 12:51           ` [PATCH v4 0/3] EAL change for using a config file for DPDK Kuba Kozak
  2 siblings, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-06-30 16:04 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain, Kuba Kozak

On Tue, Jun 27, 2017 at 12:52:38PM +0200, Jacek Piasecki wrote:
> From: Kuba Kozak <kubax.kozak@intel.com>
> 
> added function rte_eal_configure which configure
> Environment Abstraction Layer (EAL) using
> configuration structure.
> 
> Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
> Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
> ---

Thanks for looking to implement this idea, to see how it would work.
Comments on the implementation inline below.

Regards,
/Bruce

> This patch depends on cfgfile patchset with:
> "cfgfile: remove EAL dependency"
> "cfgfile: add new functions to API"
> "cfgfile: rework of load function"
> "test/cfgfile: add new unit test"
> ---
>  config/common_base                              |   1 +
>  lib/Makefile                                    |   6 +-
>  lib/librte_eal/bsdapp/eal/Makefile              |   4 +
>  lib/librte_eal/bsdapp/eal/eal.c                 | 249 ++++++++++++++---
>  lib/librte_eal/bsdapp/eal/rte_eal_version.map   |   4 +
>  lib/librte_eal/common/eal_common_cpuflags.c     |  14 +-
>  lib/librte_eal/common/eal_common_lcore.c        |  11 +-
>  lib/librte_eal/common/eal_common_options.c      |   5 +
>  lib/librte_eal/common/include/rte_eal.h         |  21 ++
>  lib/librte_eal/linuxapp/eal/Makefile            |   3 +
>  lib/librte_eal/linuxapp/eal/eal.c               | 353 ++++++++++++++++++------
>  lib/librte_eal/linuxapp/eal/rte_eal_version.map |   4 +
>  mk/rte.app.mk                                   |   2 +-
>  13 files changed, 543 insertions(+), 134 deletions(-)
> 
> diff --git a/config/common_base b/config/common_base
> index f6aafd1..c1d0e69 100644
> --- a/config/common_base
> +++ b/config/common_base
> @@ -569,6 +569,7 @@ CONFIG_RTE_LIBRTE_TIMER_DEBUG=n
>  # Compile librte_cfgfile
>  #
>  CONFIG_RTE_LIBRTE_CFGFILE=y
> +CONFIG_RTE_LIBRTE_CFGFILE_DEBUG=n

This change does not belong in this patchset. In fact, I don't think it
belongs anywhere as there are no debug statements in the cfgfile
library.

>  
>  #
>  # Compile librte_cmdline
> diff --git a/lib/Makefile b/lib/Makefile
> index 07e1fd0..fc5df3a 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -32,7 +32,11 @@
>  include $(RTE_SDK)/mk/rte.vars.mk
>  
>  DIRS-y += librte_compat
> +DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile
>  DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
> +ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
> +DEPDIRS-librte_eal := librte_cfgfile
> +endif
>  DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
>  DEPDIRS-librte_ring := librte_eal
>  DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
> @@ -41,8 +45,6 @@ DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
>  DEPDIRS-librte_mbuf := librte_eal librte_mempool
>  DIRS-$(CONFIG_RTE_LIBRTE_TIMER) += librte_timer
>  DEPDIRS-librte_timer := librte_eal
> -DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile
> -DEPDIRS-librte_cfgfile := librte_eal
>  DIRS-$(CONFIG_RTE_LIBRTE_CMDLINE) += librte_cmdline
>  DEPDIRS-librte_cmdline := librte_eal
>  DIRS-$(CONFIG_RTE_LIBRTE_ETHER) += librte_ether

I think the parts of this change - the deleteion of the line saying
cfgfile depends on EAL, and the moving of the declaration of the cfgfile
library up in the file, should go in patch 1 of the earlier cfgfile set.

> diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
> index a0f9950..d70eefb 100644
> --- a/lib/librte_eal/bsdapp/eal/Makefile
> +++ b/lib/librte_eal/bsdapp/eal/Makefile
> @@ -50,6 +50,10 @@ EXPORT_MAP := rte_eal_version.map
>  
>  LIBABIVER := 4
>  
> +ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
> +LDLIBS += -lrte_cfgfile
> +endif
> +

This should not be needed here. Other DPDK libs which depend on yet
other libs don't go modifying the LDLIBS like this.

>  # specific to bsdapp exec-env
>  SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) := eal.c
>  SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += eal_memory.c
> diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
> index 05f0c1f..7baf848 100644
> --- a/lib/librte_eal/bsdapp/eal/eal.c
> +++ b/lib/librte_eal/bsdapp/eal/eal.c
> @@ -73,6 +73,7 @@
>  #include <rte_version.h>
>  #include <rte_atomic.h>
>  #include <malloc_heap.h>
> +#include <rte_cfgfile.h>
>  
>  #include "eal_private.h"
>  #include "eal_thread.h"
> @@ -309,6 +310,18 @@ eal_get_hugepage_mem_size(void)
>  
>  /* Parse the arguments for --log-level only */
>  static void
> +eal_log_level_cfg(struct rte_cfgfile *cfg)
> +{
> +	const char *entry;
> +
> +	entry = rte_cfgfile_get_entry(cfg, "DPDK", OPT_LOG_LEVEL);
> +	if (entry)
> +		eal_parse_common_option(OPT_LOG_LEVEL_NUM, entry,
> +				&internal_config);
> +}
> +
> +/* Parse the arguments for --log-level only */
> +static void
>  eal_log_level_parse(int argc, char **argv)
>  {
>  	int opt;
> @@ -347,6 +360,58 @@ eal_log_level_parse(int argc, char **argv)
>  	optarg = old_optarg;
>  }
>  
> +/* Parse single argument */
> +static int
> +eal_parse_option(int opt, char *optarg, int option_index, char *prgname)
> +{
> +	int ret;
> +
> +	/* getopt is not happy, stop right now */
> +	if (opt == '?') {
> +		eal_usage(prgname);
> +		ret = -1;
> +		goto out;
> +	}
> +
> +	ret = eal_parse_common_option(opt, optarg, &internal_config);
> +	/* common parser is not happy */
> +	if (ret < 0) {
> +		eal_usage(prgname);
> +		ret = -1;
> +		goto out;
> +	}
> +	/* common parser handled this option */
> +	if (ret == 0)
> +		return 0;
> +
> +	switch (opt) {
> +	case 'h':
> +		eal_usage(prgname);
> +		exit(EXIT_SUCCESS);
> +		break;
> +
> +	default:
> +		if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
> +			RTE_LOG(ERR, EAL, "Option %c is not supported "
> +				"on FreeBSD\n", opt);
> +		} else if (opt >= OPT_LONG_MIN_NUM &&
> +			   opt < OPT_LONG_MAX_NUM) {
> +			RTE_LOG(ERR, EAL, "Option %s is not supported "
> +				"on FreeBSD\n",
> +				eal_long_options[option_index].name);
> +		} else {
> +			RTE_LOG(ERR, EAL, "Option %d is not supported "
> +				"on FreeBSD\n", opt);
> +		}
> +		eal_usage(prgname);
> +		ret = -1;
> +		goto out;
> +	}
> +	return 0;
> +out:
> +	return ret;
> +}
> +
>  /* Parse the argument given in the command line of the application */
>  static int
>  eal_parse_args(int argc, char **argv)
> @@ -367,45 +432,9 @@ eal_parse_args(int argc, char **argv)
>  	while ((opt = getopt_long(argc, argvopt, eal_short_options,
>  				  eal_long_options, &option_index)) != EOF) {
>  
> -		/* getopt is not happy, stop right now */
> -		if (opt == '?') {
> -			eal_usage(prgname);
> -			ret = -1;
> -			goto out;
> -		}
> -
> -		ret = eal_parse_common_option(opt, optarg, &internal_config);
> -		/* common parser is not happy */
> -		if (ret < 0) {
> -			eal_usage(prgname);
> -			ret = -1;
> -			goto out;
> -		}
> -		/* common parser handled this option */
> -		if (ret == 0)
> -			continue;
> -
> -		switch (opt) {
> -		case 'h':
> -			eal_usage(prgname);
> -			exit(EXIT_SUCCESS);
> -		default:
> -			if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
> -				RTE_LOG(ERR, EAL, "Option %c is not supported "
> -					"on FreeBSD\n", opt);
> -			} else if (opt >= OPT_LONG_MIN_NUM &&
> -				   opt < OPT_LONG_MAX_NUM) {
> -				RTE_LOG(ERR, EAL, "Option %s is not supported "
> -					"on FreeBSD\n",
> -					eal_long_options[option_index].name);
> -			} else {
> -				RTE_LOG(ERR, EAL, "Option %d is not supported "
> -					"on FreeBSD\n", opt);
> -			}
> -			eal_usage(prgname);
> -			ret = -1;
> +		ret = eal_parse_option(opt, optarg, option_index, prgname);
> +		if (ret < 0)
>  			goto out;
> -		}
>  	}
>  
>  	if (eal_adjust_config(&internal_config) != 0) {
> @@ -677,3 +706,147 @@ rte_eal_process_type(void)
>  {
>  	return rte_config.process_type;
>  }
> +
> +#ifdef RTE_LIBRTE_CFGFILE
> +#define vdev_buff_size		200
> +#define sectionname_size	20
> +static int
> +parse_vdev_devices(struct rte_cfgfile *cfg)
> +{
> +	char sectionname[sectionname_size];
> +	char buffer1[vdev_buff_size];
> +	int vdev_nb = 0;
> +	int n_entries;
> +	int i;
> +
> +	/* ----------- parsing VDEVS */
> +	snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);

move this inside the loop to be the first thing done, rather than having
it outside and then the last thing on each iteration.

> +
> +	for (vdev_nb = 1; rte_cfgfile_has_section(cfg, sectionname);
> +			vdev_nb++) {
> +		n_entries = rte_cfgfile_section_num_entries(cfg, sectionname);
> +
> +		struct rte_cfgfile_entry entries[n_entries];
> +
> +		if (n_entries != rte_cfgfile_section_entries(cfg, sectionname,
> +				entries, n_entries)) {
> +			rte_eal_init_alert("Unexpected fault.");
> +			rte_errno = EFAULT;
> +			return -1;
> +		}
> +
> +		buffer1[0] = 0;
> +		for (i = 0; i < n_entries; i++) {
> +			if (strlen(entries[i].value)) {
> +
> +				if ((strlen(buffer1) +
> +						strlen(entries[i].name) +
> +						strlen(entries[i].value) + 3)
> +						>= vdev_buff_size)
> +					goto buff_size_err;
> +				strcat(buffer1, entries[i].name);
> +				strcat(buffer1, "=");
> +				strcat(buffer1, entries[i].value);
> +			} else {
> +				if ((strlen(buffer1) +
> +						strlen(entries[i].name) + 2)
> +						>= vdev_buff_size)
> +					goto buff_size_err;
> +				strcat(buffer1, entries[i].name);
> +			}
> +

Doing these strlen calls and calculations that way is horribly
inefficient IMHO, even if this is not in a perf-critical path. Instead,
I would look to start with a buf_len variable which tracks the available
buffer size, and a buf_ptr variable which points to the first available
character. Then do the following each time you want to add something:

	ret = snprintf(buf_ptr, buf_len, "%s%s%s", entries[i].name,
		entries[i].value[0] != '\0' ? "=" : "",
		entries[i].value);
	if (ret >= buf_len)
		goto buff_size_err;
	buf_ptr += ret;
	buf_len -= ret;

Shorter and simpler, as well as being more efficient as it does not need
any string length computation calls, using snprintf safely instead.

> +			if (i < (n_entries - 1))
> +				strcat(buffer1, ",");

You can add this bit into the above snprintf too, in a similar way to
how I optionally added the "=".

> +		}
> +
> +		/* parsing vdev */
> +		if (rte_eal_devargs_add(RTE_DEVTYPE_VIRTUAL,
> +				buffer1) < 0) {
> +			return -1;
> +		}
> +		snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
> +	}
> +	/* ----------- parsing VDEVS */
> +	return 0;
> +
> +buff_size_err:
> +	printf("parse_vdev_devices(): buffer size is to small\n");
> +	return -1;

If the code is reworked so that there is only one place where this error
occurs, remove the label and goto, and just put the error handling
inline in the code.

> +}
> +
> +static void
> +eal_getopt(const char *str, int *opt, int *option_index)
> +{
> +	int i;
> +
> +	*opt = '?';
> +	*option_index = 0;
> +
> +	if (strlen(str) == 1) {
> +		*opt = *str;
> +		return;
> +	}
> +
> +	for (i = 0; eal_long_options[i].name != NULL; i++) {
> +		if (strcmp(str, eal_long_options[i].name) == 0) {
> +			*opt = eal_long_options[i].val;
> +			*option_index = i;
> +			break;
> +		}
> +	}
> +}
> +
> +int
> +rte_eal_configure(struct rte_cfgfile *cfg, char *prgname)
> +{
> +	int n_entries;
> +	int i;
> +	int opt;
> +	int option_index;
> +
> +	if (cfg == NULL) {
> +		rte_errno = -EINVAL;
> +		return -1;
> +	}
> +
> +	n_entries = rte_cfgfile_section_num_entries(cfg, "DPDK");
> +
> +	if (n_entries < 1) {
> +		printf("No DPDK section entries in cfgfile object\n");
> +		return 0;
> +	}
> +
> +	struct rte_cfgfile_entry entries[n_entries];
> +
> +	if (n_entries !=
> +			rte_cfgfile_section_entries(cfg, "DPDK", entries,
> +					n_entries)) {
> +		rte_eal_init_alert("Unexpected fault.");
> +		rte_errno = EFAULT;
> +		return -1;
> +	}
> +
> +	eal_reset_internal_config(&internal_config);
> +
> +	/* set log level as early as possible */
> +	eal_log_level_cfg(cfg);
> +
> +	for (i = 0; i < n_entries; i++) {
> +		eal_getopt(entries[i].name, &opt, &option_index);
> +
> +		if (eal_parse_option(opt, entries[i].value,
> +				option_index, prgname) != 0) {
> +			rte_eal_init_alert("Invalid config file arguments.");
> +			rte_errno = EINVAL;
> +			return -1;
> +		}
> +	}
> +
> +	if (parse_vdev_devices(cfg) < 0) {
> +		rte_eal_init_alert("Couldn't parse vdevs");
> +		rte_errno = ENOMEM;
> +		return -1;
> +	}
> +	return 0;
> +}
> +#endif
> diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
> index 2e48a73..a939b03 100644
> --- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
> +++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
> @@ -193,3 +193,7 @@ DPDK_17.05 {
>  	vfio_get_group_no;
>  
>  } DPDK_17.02;
> +
> +DPDK_17.08 {
> +	rte_eal_configure;
> +} DPDK_17.05;
> diff --git a/lib/librte_eal/common/eal_common_cpuflags.c b/lib/librte_eal/common/eal_common_cpuflags.c
> index 9a2d080..6a365f3 100644
> --- a/lib/librte_eal/common/eal_common_cpuflags.c
> +++ b/lib/librte_eal/common/eal_common_cpuflags.c
> @@ -50,12 +50,18 @@ rte_cpu_check_supported(void)
>  int
>  rte_cpu_is_supported(void)
>  {
> +	static int run_once;
> +	static int ret;
>  	/* This is generated at compile-time by the build system */
>  	static const enum rte_cpu_flag_t compile_time_flags[] = {
>  			RTE_COMPILE_TIME_CPUFLAGS
>  	};
>  	unsigned count = RTE_DIM(compile_time_flags), i;
> -	int ret;
> +
> +	/* No need to calculate this function again if we know the result */
> +	if (run_once)
> +		return ret;
> +	run_once = 1;
>  
>  	for (i = 0; i < count; i++) {
>  		ret = rte_cpu_get_flag_enabled(compile_time_flags[i]);
> @@ -64,16 +70,16 @@ rte_cpu_is_supported(void)
>  			fprintf(stderr,
>  				"ERROR: CPU feature flag lookup failed with error %d\n",
>  				ret);
> -			return 0;
> +			return ret = 0;
>  		}
>  		if (!ret) {
>  			fprintf(stderr,
>  			        "ERROR: This system does not support \"%s\".\n"
>  			        "Please check that RTE_MACHINE is set correctly.\n",
>  			        rte_cpu_get_flag_name(compile_time_flags[i]));
> -			return 0;
> +			return ret = 0;
>  		}
>  	}
>  
> -	return 1;
> +	return ret = 1;
>  }

This is not a bad change in and of itself. However, I'm not sure it
belongs in this patchset. With the new configure call, we should still
only be calling these initialization functions once - in eal_init().


> diff --git a/lib/librte_eal/common/eal_common_lcore.c b/lib/librte_eal/common/eal_common_lcore.c
> index 84fa0cb..ce3ef34 100644
> --- a/lib/librte_eal/common/eal_common_lcore.c
> +++ b/lib/librte_eal/common/eal_common_lcore.c
> @@ -53,11 +53,18 @@
>  int
>  rte_eal_cpu_init(void)
>  {
> +	static int run_once;
> +	static int ret;
>  	/* pointer to global configuration */
>  	struct rte_config *config = rte_eal_get_configuration();
>  	unsigned lcore_id;
>  	unsigned count = 0;
>  
> +	/* No need to calculate this function again if we know the result */
> +	if (run_once)
> +		return ret;
> +	run_once = 1;
> +
>  	/*
>  	 * Parse the maximum set of logical cores, detect the subset of running
>  	 * ones and enable them by default.
> @@ -91,7 +98,7 @@ rte_eal_cpu_init(void)
>  				"RTE_MAX_NUMA_NODES (%d)\n",
>  				lcore_config[lcore_id].socket_id,
>  				RTE_MAX_NUMA_NODES);
> -			return -1;
> +			return ret = -1;
>  #endif
>  		}
>  		RTE_LOG(DEBUG, EAL, "Detected lcore %u as "
> @@ -107,5 +114,5 @@ rte_eal_cpu_init(void)
>  		RTE_MAX_LCORE);
>  	RTE_LOG(INFO, EAL, "Detected %u lcore(s)\n", config->lcore_count);
>  
> -	return 0;
> +	return ret = 0;
>  }

As above, should not be needed.

> diff --git a/lib/librte_eal/common/eal_common_options.c b/lib/librte_eal/common/eal_common_options.c
> index f470195..138a27f 100644
> --- a/lib/librte_eal/common/eal_common_options.c
> +++ b/lib/librte_eal/common/eal_common_options.c
> @@ -131,8 +131,13 @@ static int core_parsed;
>  void
>  eal_reset_internal_config(struct internal_config *internal_cfg)
>  {
> +	static int run_once;
>  	int i;
>  
> +	if (run_once)
> +		return;
> +	run_once = 1;
> +
>  	internal_cfg->memory = 0;
>  	internal_cfg->force_nrank = 0;
>  	internal_cfg->force_nchannel = 0;

I don't think for this function this is the way to do things, given that
the function does one job - resetting the internal config - and the new
flag just disables it. It's different from e.g. the cpu check which is
able to cache a return value.

Instead, this should be tracked at the higher eal level, and not call
the function at all if it is unneeded. This mean that eal_init should
check itself if configure has already called the function and then skip
calling it itself.

> diff --git a/lib/librte_eal/common/include/rte_eal.h b/lib/librte_eal/common/include/rte_eal.h
> index abf020b..e0705de 100644
> --- a/lib/librte_eal/common/include/rte_eal.h
> +++ b/lib/librte_eal/common/include/rte_eal.h
> @@ -46,6 +46,8 @@
>  #include <rte_per_lcore.h>
>  #include <rte_config.h>
>  
> +struct rte_cfgfile; /* forward declaration of struct */
> +
>  #ifdef __cplusplus
>  extern "C" {
>  #endif
> @@ -188,6 +190,25 @@ int rte_eal_iopl_init(void);
>   */
>  int rte_eal_init(int argc, char **argv);
>  
> +#ifdef RTE_LIBRTE_CFGFILE
> +/**
> + * Initialize the Environment Abstraction Layer (EAL) using
> + * configuration structure
> + *
> + * @param cfg
> + *   pointer to config file structure.
> + *   If 0 - function free allocated for **cfg_argv memory

Don't understand this comment.

> + * @param prgname
> + *   pointer to string with execution path
> + *
> + * @return
> + *  - On success, return 0
> + *  - On failure, returns -1.
> + */
> +int
> +rte_eal_configure(struct rte_cfgfile *cfg, char *prgname);
> +#endif
> +
>  /**
>   * Check if a primary process is currently alive
>   *
> diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
> index 640afd0..656033e 100644
> --- a/lib/librte_eal/linuxapp/eal/Makefile
> +++ b/lib/librte_eal/linuxapp/eal/Makefile
> @@ -50,6 +50,9 @@ LDLIBS += -ldl
>  LDLIBS += -lpthread
>  LDLIBS += -lgcc_s
>  LDLIBS += -lrt
> +ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
> +LDLIBS += -lrte_cfgfile
> +endif
>  
>  # specific to linuxapp exec-env
>  SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) := eal.c
> diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
> index 7c78f2d..f5973f4 100644
> --- a/lib/librte_eal/linuxapp/eal/eal.c
> +++ b/lib/librte_eal/linuxapp/eal/eal.c
> @@ -78,6 +78,9 @@
>  #include <rte_version.h>
>  #include <rte_atomic.h>
>  #include <malloc_heap.h>
> +#ifdef RTE_LIBRTE_CFGFILE
> +#include <rte_cfgfile.h>
> +#endif
>  
>  #include "eal_private.h"
>  #include "eal_thread.h"
> @@ -478,6 +481,20 @@ eal_parse_vfio_intr(const char *mode)
>  	return -1;
>  }
>  
> +#ifdef RTE_LIBRTE_CFGFILE
> +/* Parse the arguments for --log-level only */
> +static void
> +eal_log_level_cfg(struct rte_cfgfile *cfg)
> +{
> +	const char *entry;
> +
> +	entry = rte_cfgfile_get_entry(cfg, "DPDK", OPT_LOG_LEVEL);
> +	if (entry)
> +		eal_parse_common_option(OPT_LOG_LEVEL_NUM, entry,
> +				&internal_config);
> +}
> +#endif
> +

This looks the same as the BSD version. Can it go in a common file?

>  /* Parse the arguments for --log-level only */
>  static void
>  eal_log_level_parse(int argc, char **argv)
> @@ -515,119 +532,135 @@ eal_log_level_parse(int argc, char **argv)
>  	optarg = old_optarg;
>  }
>  
> -/* Parse the argument given in the command line of the application */
> +/* Parse single argument */
>  static int
> -eal_parse_args(int argc, char **argv)
> +eal_parse_option(int opt, char *optarg, int option_index, char *prgname)
>  {
> -	int opt, ret;
> -	char **argvopt;
> -	int option_index;
> -	char *prgname = argv[0];
> -	const int old_optind = optind;
> -	const int old_optopt = optopt;
> -	char * const old_optarg = optarg;
> +	int ret;
>  
> -	argvopt = argv;
> -	optind = 1;
> +	/* getopt is not happy, stop right now */
> +	if (opt == '?') {
> +		eal_usage(prgname);
> +		ret = -1;
> +		goto out;
> +	}
>  
> -	while ((opt = getopt_long(argc, argvopt, eal_short_options,
> -				  eal_long_options, &option_index)) != EOF) {
> +	ret = eal_parse_common_option(opt, optarg, &internal_config);
> +	/* common parser is not happy */
> +	if (ret < 0) {
> +		eal_usage(prgname);
> +		ret = -1;
> +		goto out;
> +	}
> +	/* common parser handled this option */
> +	if (ret == 0)
> +		return 0;
>  
> -		/* getopt is not happy, stop right now */
> -		if (opt == '?') {
> +	switch (opt) {
> +	case 'h':
> +		eal_usage(prgname);
> +		exit(EXIT_SUCCESS);
> +		break;
> +
> +	/* long options */
> +	case OPT_XEN_DOM0_NUM:
> +#ifdef RTE_LIBRTE_XEN_DOM0
> +		internal_config.xen_dom0_support = 1;
> +		break;
> +#else
> +		RTE_LOG(ERR, EAL, "Can't support DPDK app "
> +			"running on Dom0, please configure"
> +			" RTE_LIBRTE_XEN_DOM0=y\n");
> +		ret = -1;
> +		goto out;
> +#endif
> +
> +	case OPT_HUGE_DIR_NUM:
> +		internal_config.hugepage_dir = optarg;
> +		break;
> +
> +	case OPT_FILE_PREFIX_NUM:
> +		internal_config.hugefile_prefix = optarg;
> +		break;
> +
> +	case OPT_SOCKET_MEM_NUM:
> +		if (eal_parse_socket_mem(optarg) < 0) {
> +			RTE_LOG(ERR, EAL, "invalid parameters for --"
> +					OPT_SOCKET_MEM "\n");
>  			eal_usage(prgname);
>  			ret = -1;
>  			goto out;
>  		}
> +		break;
>  
> -		ret = eal_parse_common_option(opt, optarg, &internal_config);
> -		/* common parser is not happy */
> -		if (ret < 0) {
> +	case OPT_BASE_VIRTADDR_NUM:
> +		if (eal_parse_base_virtaddr(optarg) < 0) {
> +			RTE_LOG(ERR, EAL, "invalid parameter for --"
> +					OPT_BASE_VIRTADDR "\n");
>  			eal_usage(prgname);
>  			ret = -1;
>  			goto out;
>  		}
> -		/* common parser handled this option */
> -		if (ret == 0)
> -			continue;
> +		break;
>  
> -		switch (opt) {
> -		case 'h':
> +	case OPT_VFIO_INTR_NUM:
> +		if (eal_parse_vfio_intr(optarg) < 0) {
> +			RTE_LOG(ERR, EAL, "invalid parameters for --"
> +					OPT_VFIO_INTR "\n");
>  			eal_usage(prgname);
> -			exit(EXIT_SUCCESS);
> -
> -		/* long options */
> -		case OPT_XEN_DOM0_NUM:
> -#ifdef RTE_LIBRTE_XEN_DOM0
> -			internal_config.xen_dom0_support = 1;
> -#else
> -			RTE_LOG(ERR, EAL, "Can't support DPDK app "
> -				"running on Dom0, please configure"
> -				" RTE_LIBRTE_XEN_DOM0=y\n");
>  			ret = -1;
>  			goto out;
> -#endif
> -			break;
> +		}
> +		break;
>  
> -		case OPT_HUGE_DIR_NUM:
> -			internal_config.hugepage_dir = optarg;
> -			break;
> +	case OPT_CREATE_UIO_DEV_NUM:
> +		internal_config.create_uio_dev = 1;
> +		break;
>  
> -		case OPT_FILE_PREFIX_NUM:
> -			internal_config.hugefile_prefix = optarg;
> -			break;
> +	default:
> +		if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
> +			RTE_LOG(ERR, EAL, "Option %c is not supported "
> +				"on Linux\n", opt);
> +		} else if (opt >= OPT_LONG_MIN_NUM &&
> +			   opt < OPT_LONG_MAX_NUM) {
> +			RTE_LOG(ERR, EAL, "Option %s is not supported "
> +				"on Linux\n",
> +				eal_long_options[option_index].name);
> +		} else {
> +			RTE_LOG(ERR, EAL, "Option %d is not supported "
> +				"on Linux\n", opt);
> +		}
> +		eal_usage(prgname);
> +		ret = -1;
> +		goto out;
> +	}
>  
> -		case OPT_SOCKET_MEM_NUM:
> -			if (eal_parse_socket_mem(optarg) < 0) {
> -				RTE_LOG(ERR, EAL, "invalid parameters for --"
> -						OPT_SOCKET_MEM "\n");
> -				eal_usage(prgname);
> -				ret = -1;
> -				goto out;
> -			}
> -			break;
> +	return 0;
> +out:
> +	return ret;
> +}
>  
> -		case OPT_BASE_VIRTADDR_NUM:
> -			if (eal_parse_base_virtaddr(optarg) < 0) {
> -				RTE_LOG(ERR, EAL, "invalid parameter for --"
> -						OPT_BASE_VIRTADDR "\n");
> -				eal_usage(prgname);
> -				ret = -1;
> -				goto out;
> -			}
> -			break;
> +/* Parse the argument given in the command line of the application */
> +static int
> +eal_parse_args(int argc, char **argv)
> +{
> +	int opt, ret;
> +	char **argvopt;
> +	int option_index;
> +	char *prgname = argv[0];
> +	const int old_optind = optind;
> +	const int old_optopt = optopt;
> +	char * const old_optarg = optarg;
>  
> -		case OPT_VFIO_INTR_NUM:
> -			if (eal_parse_vfio_intr(optarg) < 0) {
> -				RTE_LOG(ERR, EAL, "invalid parameters for --"
> -						OPT_VFIO_INTR "\n");
> -				eal_usage(prgname);
> -				ret = -1;
> -				goto out;
> -			}
> -			break;
> +	argvopt = argv;
> +	optind = 1;
>  
> -		case OPT_CREATE_UIO_DEV_NUM:
> -			internal_config.create_uio_dev = 1;
> -			break;
> +	while ((opt = getopt_long(argc, argvopt, eal_short_options,
> +				  eal_long_options, &option_index)) != EOF) {
>  
> -		default:
> -			if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
> -				RTE_LOG(ERR, EAL, "Option %c is not supported "
> -					"on Linux\n", opt);
> -			} else if (opt >= OPT_LONG_MIN_NUM &&
> -				   opt < OPT_LONG_MAX_NUM) {
> -				RTE_LOG(ERR, EAL, "Option %s is not supported "
> -					"on Linux\n",
> -					eal_long_options[option_index].name);
> -			} else {
> -				RTE_LOG(ERR, EAL, "Option %d is not supported "
> -					"on Linux\n", opt);
> -			}
> -			eal_usage(prgname);
> -			ret = -1;
> +		ret = eal_parse_option(opt, optarg, option_index, prgname);
> +		if (ret < 0)
>  			goto out;
> -		}
>  	}
>  
>  	if (eal_adjust_config(&internal_config) != 0) {
> @@ -995,3 +1028,149 @@ rte_eal_check_module(const char *module_name)
>  	/* Module has been found */
>  	return 1;
>  }
> +
> +#ifdef RTE_LIBRTE_CFGFILE
> +#define vdev_buff_size		200
> +#define sectionname_size	20
> +static int
> +parse_vdev_devices(struct rte_cfgfile *cfg)
> +{
> +	char sectionname[sectionname_size];
> +	char buffer1[vdev_buff_size];
> +	int vdev_nb = 0;
> +	int n_entries;
> +
> +	int i;
> +
> +	/* ----------- parsing VDEVS */
> +	snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
> +
> +	for (vdev_nb = 1; rte_cfgfile_has_section(cfg, sectionname);
> +			vdev_nb++) {
> +		n_entries = rte_cfgfile_section_num_entries(cfg, sectionname);
> +
> +		struct rte_cfgfile_entry entries[n_entries];
> +
> +
> +		if (n_entries != rte_cfgfile_section_entries(cfg, sectionname,
> +				entries, n_entries)) {
> +			rte_eal_init_alert("Unexpected fault.");
> +			rte_errno = EFAULT;
> +			return -1;
> +		}
> +
> +		buffer1[0] = 0;
> +		for (i = 0; i < n_entries; i++) {
> +			if (strlen(entries[i].value)) {
> +
> +				if ((strlen(buffer1) +
> +						strlen(entries[i].name) +
> +						strlen(entries[i].value) + 3)
> +						>= vdev_buff_size)
> +					goto buff_size_err;
> +				strcat(buffer1, entries[i].name);
> +				strcat(buffer1, "=");
> +				strcat(buffer1, entries[i].value);
> +			} else {
> +				if ((strlen(buffer1) +
> +						strlen(entries[i].name) + 2)
> +						>= vdev_buff_size)
> +					goto buff_size_err;
> +				strcat(buffer1, entries[i].name);
> +			}
> +
> +			if (i < (n_entries - 1))
> +				strcat(buffer1, ",");
> +		}
> +
> +		/* parsing vdev */
> +		if (rte_eal_devargs_add(RTE_DEVTYPE_VIRTUAL,
> +				buffer1) < 0) {
> +			return -1;
> +		}
> +		snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
> +	}
> +	/* ----------- parsing VDEVS */
> +	return 0;
> +
> +buff_size_err:
> +	printf("parse_vdev_devices(): buffer size is to small\n");
> +	return -1;
> +}
> +
> +static void
> +eal_getopt(const char *str, int *opt, int *option_index)
> +{
> +	int i;
> +
> +	*opt = '?';
> +	*option_index = 0;
> +
> +	if (strlen(str) == 1) {
> +		*opt = *str;
> +		return;
> +	}
> +
> +	for (i = 0; eal_long_options[i].name != NULL; i++) {
> +		if (strcmp(str, eal_long_options[i].name) == 0) {
> +			*opt = eal_long_options[i].val;
> +			*option_index = i;
> +			break;
> +		}
> +	}
> +}
> +
> +int
> +rte_eal_configure(struct rte_cfgfile *cfg, char *prgname)
> +{
> +	int n_entries;
> +	int i;
> +	int opt;
> +	int option_index;
> +
> +	if (cfg == NULL) {
> +		rte_errno = -EINVAL;
> +		return -1;
> +	}
> +
> +	n_entries = rte_cfgfile_section_num_entries(cfg, "DPDK");
> +
> +	if (n_entries < 1) {
> +		printf("No DPDK section entries in cfgfile object\n");
> +		return 0;
> +	}
> +
> +	struct rte_cfgfile_entry entries[n_entries];
> +
> +	if (n_entries !=
> +			rte_cfgfile_section_entries(cfg, "DPDK", entries,
> +					n_entries)) {
> +		rte_eal_init_alert("Unexpected fault.");
> +		rte_errno = EFAULT;
> +		return -1;
> +	}
> +
> +	eal_reset_internal_config(&internal_config);
> +
> +	/* set log level as early as possible */
> +	eal_log_level_cfg(cfg);
> +
> +	for (i = 0; i < n_entries; i++) {
> +		eal_getopt(entries[i].name, &opt, &option_index);
> +
> +		if (eal_parse_option(opt, entries[i].value,
> +				option_index, prgname) != 0) {
> +			rte_eal_init_alert("Invalid config file arguments.");
> +			rte_errno = EINVAL;
> +			return -1;
> +		}
> +	}
> +
> +	if (parse_vdev_devices(cfg) < 0) {
> +		rte_eal_init_alert("Couldn't parse vdevs");
> +		rte_errno = ENOMEM;
> +		return -1;
> +	}
> +	return 0;
> +}
> +#endif

Can this code in the #ifdef #endif block above go in a common file, as
again it looks very alike the BSD version? 

> diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
> index 670bab3..c93e6d9 100644
> --- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
> +++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
> @@ -198,3 +198,7 @@ DPDK_17.05 {
>  	vfio_get_group_no;
>  
>  } DPDK_17.02;
> +
> +DPDK_17.08 {
> +	rte_eal_configure;
> +} DPDK_17.05;
> diff --git a/mk/rte.app.mk b/mk/rte.app.mk
> index bcaf1b3..642af92 100644
> --- a/mk/rte.app.mk
> +++ b/mk/rte.app.mk
> @@ -80,7 +80,6 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_POWER)          += -lrte_power
>  
>  _LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER)          += -lrte_timer
>  _LDLIBS-$(CONFIG_RTE_LIBRTE_EFD)            += -lrte_efd
> -_LDLIBS-$(CONFIG_RTE_LIBRTE_CFGFILE)        += -lrte_cfgfile
>  
>  _LDLIBS-y += --whole-archive
>  
> @@ -96,6 +95,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL)        += -lrte_mempool
>  _LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING)   += -lrte_mempool_ring
>  _LDLIBS-$(CONFIG_RTE_LIBRTE_RING)           += -lrte_ring
>  _LDLIBS-$(CONFIG_RTE_LIBRTE_EAL)            += -lrte_eal
> +_LDLIBS-$(CONFIG_RTE_LIBRTE_CFGFILE)        += -lrte_cfgfile
>  _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline
>  _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
>  
> -- 
> 2.7.4
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v3 0/3] EAL change for using a config file for DPDK
  2017-06-27 10:52       ` [PATCH v3 0/3] EAL change for using a config file for DPDK Jacek Piasecki
                           ` (2 preceding siblings ...)
  2017-06-27 10:52         ` [PATCH v3 3/3] app/testpmd: add parse arguments from JSON config file Jacek Piasecki
@ 2017-07-05  0:00         ` Thomas Monjalon
  3 siblings, 0 replies; 70+ messages in thread
From: Thomas Monjalon @ 2017-07-05  0:00 UTC (permalink / raw)
  To: Jacek Piasecki, bruce.richardson; +Cc: dev, deepak.k.jain

27/06/2017 12:52, Jacek Piasecki:
> This patchset introduce a mechanism for running dpdk application with parameters
> provided by configuration file.
> 
> A new API for EAL takes a config file data type - either loaded from file,
> or built up programmatically in the application - and extracts DPDK parameters
> from it to be used when eal init is called. This allows apps to have
> an alternative method to configure EAL, other than via command-line parameters.

I think we should focus on better API for apps instead of forging
the default EAL config into the stone of a config file.

Anyway, we do not have enough time in 17.08 release cycle to properly
discuss this important topic.
I hope it opens the discussion in order to have a clear view of what
we could change and integrate in 17.11.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v4 0/5] Rework cfgfile API to enable apps config file support
  2017-06-27 10:52         ` [PATCH v3 1/3] eal: add functions parsing EAL arguments Jacek Piasecki
  2017-06-30 16:04           ` Bruce Richardson
@ 2017-07-10 12:44           ` Jacek Piasecki
  2017-07-10 12:44             ` [PATCH v4 1/5] cfgfile: remove EAL dependency Jacek Piasecki
                               ` (5 more replies)
  2017-07-10 12:51           ` [PATCH v4 0/3] EAL change for using a config file for DPDK Kuba Kozak
  2 siblings, 6 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-07-10 12:44 UTC (permalink / raw)
  To: dev
  Cc: bruce.richardson, deepak.k.jain, kubax.kozak,
	michalx.k.jastrzebski, Jacek Piasecki

New API for cfgfile library allows to create a cfgfile at runtime, add new
section, add entry in a section, update existing entry and save cfgfile
structure to INI file - opens up the possibility to have applications
dynamically build up a proper DPDK configuration, rather than
having to have a pre-existing one. Due the new API functions, simplification
of load() function was made. One new unit test to TEST app was added. It
contains an example of a large INI file whose parsing requires multiple
reallocation of memory.

---
v4:	
	Change members of structure cfgfile:
	- struct *sections[] to *sections
	- struct *entries[] to *entries
	- remove free_sections and free_entries
	Rework of existing cfgfile API functions to work with modified
	rte_cfgfile struct.
	Rework of malloc/realloc implementation due rte_cfgfile struct change,
	reducing mulitiple mallocs.
	Change return error codes for all library functions (errno.h)
	Checkpatch fixes
v3: 
	split one patchset into two distinct patchsets:
	1. cfgfile library and TEST app changes
	2. EAL changes and examples (this patchset depends on cfgfile)
v2:
  lib eal:
  	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
	Now this function load data from cfg structure and did initial
	initialization of EAL arguments. Vdev argument are stored in different
	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
	function it is necessary to call rte_eal_init to complete EAL
	initialization. There is no more merging arguments from different
	sources (cfg file and command line).
  	Added non_eal_configure to testpmd application.
	Function maintain the same functionality as rte_eal_configure but
	for non-eal arguments. 
  	Added config JSON feature to testpmd last patch from patchset contain
	example showing use of .json configuration files.

  lib cfgfile:
  	Rework of add_section(), add_entry() new implementation
  	New members allocated_entries/sections, free_entries/sections
	in rte_cfgfile structure, change in array of pointers
	**sections, **entries instead of *sections[], *entries[]
  	Add  set_entry() to update/overwrite already existing entry in cfgfile
	struct
  	Add save() function to save on disc cfgfile structure in INI format
  	Rework of existing load() function  simplifying the code
  	Add unit test realloc_sections() in TEST app for testing realloc/malloc
	of new API functions, add test for save() function

Jacek Piasecki (5):
  cfgfile: remove EAL dependency
  cfgfile: change existing API functions
  cfgfile: add new functions to API
  cfgfile: rework of load function
  test/cfgfile: add new unit test

 lib/Makefile                                     |   3 +-
 lib/librte_cfgfile/Makefile                      |   1 +
 lib/librte_cfgfile/rte_cfgfile.c                 | 417 ++++++++++++++---------
 lib/librte_cfgfile/rte_cfgfile.h                 |  82 ++++-
 lib/librte_cfgfile/rte_cfgfile_version.map       |  11 +
 test/test/test_cfgfile.c                         |  40 +++
 test/test/test_cfgfiles/etc/realloc_sections.ini | 128 +++++++
 7 files changed, 514 insertions(+), 168 deletions(-)
 create mode 100644 test/test/test_cfgfiles/etc/realloc_sections.ini

-- 
2.7.4

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v4 1/5] cfgfile: remove EAL dependency
  2017-07-10 12:44           ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Jacek Piasecki
@ 2017-07-10 12:44             ` Jacek Piasecki
  2017-08-30 17:58               ` Bruce Richardson
  2017-07-10 12:44             ` [PATCH v4 2/5] cfgfile: change existing API functions Jacek Piasecki
                               ` (4 subsequent siblings)
  5 siblings, 1 reply; 70+ messages in thread
From: Jacek Piasecki @ 2017-07-10 12:44 UTC (permalink / raw)
  To: dev
  Cc: bruce.richardson, deepak.k.jain, kubax.kozak,
	michalx.k.jastrzebski, Jacek Piasecki

This patch removes the dependency to EAL in cfgfile library.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 lib/Makefile                     |  3 +--
 lib/librte_cfgfile/Makefile      |  1 +
 lib/librte_cfgfile/rte_cfgfile.c | 29 +++++++++++++++++------------
 3 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/lib/Makefile b/lib/Makefile
index 07e1fd0..cf47a2a 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -32,6 +32,7 @@
 include $(RTE_SDK)/mk/rte.vars.mk
 
 DIRS-y += librte_compat
+DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile
 DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
 DEPDIRS-librte_ring := librte_eal
@@ -41,8 +42,6 @@ DIRS-$(CONFIG_RTE_LIBRTE_MBUF) += librte_mbuf
 DEPDIRS-librte_mbuf := librte_eal librte_mempool
 DIRS-$(CONFIG_RTE_LIBRTE_TIMER) += librte_timer
 DEPDIRS-librte_timer := librte_eal
-DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile
-DEPDIRS-librte_cfgfile := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_CMDLINE) += librte_cmdline
 DEPDIRS-librte_cmdline := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_ETHER) += librte_ether
diff --git a/lib/librte_cfgfile/Makefile b/lib/librte_cfgfile/Makefile
index 755ef11..0bee43e 100644
--- a/lib/librte_cfgfile/Makefile
+++ b/lib/librte_cfgfile/Makefile
@@ -38,6 +38,7 @@ LIB = librte_cfgfile.a
 
 CFLAGS += -O3
 CFLAGS += $(WERROR_FLAGS)
+CFLAGS += -I$(SRCDIR)/../librte_eal/common/include
 
 EXPORT_MAP := rte_cfgfile_version.map
 
diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
index b54a523..c6ae3e3 100644
--- a/lib/librte_cfgfile/rte_cfgfile.c
+++ b/lib/librte_cfgfile/rte_cfgfile.c
@@ -36,7 +36,6 @@
 #include <string.h>
 #include <ctype.h>
 #include <rte_common.h>
-#include <rte_string_fns.h>
 
 #include "rte_cfgfile.h"
 
@@ -258,19 +257,25 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 
 			struct rte_cfgfile_section *sect =
 				cfg->sections[curr_section];
-			int n;
+
 			char *split[2] = {NULL};
-			n = rte_strsplit(buffer, sizeof(buffer), split, 2, '=');
-			if (flags & CFG_FLAG_EMPTY_VALUES) {
-				if ((n < 1) || (n > 2)) {
-					printf("Error at line %d - cannot split string, n=%d\n",
-					       lineno, n);
-					goto error1;
-				}
+			split[0] = buffer;
+			split[1] = memchr(buffer, '=', len);
+
+			/* when delimeter not found */
+			if (split[1] == NULL) {
+				printf("Error at line %d - cannot "
+					"split string\n", lineno);
+				goto error1;
 			} else {
-				if (n != 2) {
-					printf("Error at line %d - cannot split string, n=%d\n",
-					       lineno, n);
+				/* when delimeter found */
+				*split[1] = '\0';
+				split[1]++;
+
+				if (!(flags & CFG_FLAG_EMPTY_VALUES) &&
+						(*split[1] == '\0')) {
+					printf("Error at line %d - cannot "
+						"split string\n", lineno);
 					goto error1;
 				}
 			}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v4 2/5] cfgfile: change existing API functions
  2017-07-10 12:44           ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Jacek Piasecki
  2017-07-10 12:44             ` [PATCH v4 1/5] cfgfile: remove EAL dependency Jacek Piasecki
@ 2017-07-10 12:44             ` Jacek Piasecki
  2017-08-30 20:07               ` Bruce Richardson
  2017-07-10 12:44             ` [PATCH v4 3/5] cfgfile: add new functions to API Jacek Piasecki
                               ` (3 subsequent siblings)
  5 siblings, 1 reply; 70+ messages in thread
From: Jacek Piasecki @ 2017-07-10 12:44 UTC (permalink / raw)
  To: dev
  Cc: bruce.richardson, deepak.k.jain, kubax.kozak,
	michalx.k.jastrzebski, Jacek Piasecki

Change to flat arrays in cfgfile struct force slightly
diffrent data access for most of cfgfile functions.
This patch provides necessary changes in existing API.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 lib/librte_cfgfile/rte_cfgfile.c | 120 +++++++++++++++++++--------------------
 lib/librte_cfgfile/rte_cfgfile.h |   6 +-
 2 files changed, 62 insertions(+), 64 deletions(-)

diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
index c6ae3e3..50fe37a 100644
--- a/lib/librte_cfgfile/rte_cfgfile.c
+++ b/lib/librte_cfgfile/rte_cfgfile.c
@@ -35,6 +35,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
+#include <errno.h>
 #include <rte_common.h>
 
 #include "rte_cfgfile.h"
@@ -42,13 +43,15 @@
 struct rte_cfgfile_section {
 	char name[CFG_NAME_LEN];
 	int num_entries;
-	struct rte_cfgfile_entry *entries[0];
+	int allocated_entries;
+	struct rte_cfgfile_entry *entries;
 };
 
 struct rte_cfgfile {
 	int flags;
 	int num_sections;
-	struct rte_cfgfile_section *sections[0];
+	int allocated_sections;
+	struct rte_cfgfile_section *sections;
 };
 
 /** when we resize a file structure, how many extra entries
@@ -104,6 +107,19 @@ _strip(char *str, unsigned len)
 	return newlen;
 }
 
+static struct rte_cfgfile_section *
+_get_section(struct rte_cfgfile *cfg, const char *sectionname)
+{
+	int i;
+
+	for (i = 0; i < cfg->num_sections; i++) {
+		if (strncmp(cfg->sections[i].name, sectionname,
+				sizeof(cfg->sections[0].name)) == 0)
+			return &cfg->sections[i];
+	}
+	return NULL;
+}
+
 static int
 rte_cfgfile_check_params(const struct rte_cfgfile_parameters *params)
 {
@@ -168,17 +184,17 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 	if (flags & CFG_FLAG_GLOBAL_SECTION) {
 		curr_section = 0;
 		allocated_entries = CFG_ALLOC_ENTRY_BATCH;
-		cfg->sections[curr_section] = malloc(
-			sizeof(*cfg->sections[0]) +
-			sizeof(cfg->sections[0]->entries[0]) *
+		cfg->sections = malloc(
+			sizeof(cfg->sections[0]) +
+			sizeof(cfg->sections[0].entries) *
 			allocated_entries);
-		if (cfg->sections[curr_section] == NULL) {
+		if (cfg->sections == NULL) {
 			printf("Error - no memory for global section\n");
 			goto error1;
 		}
 
-		snprintf(cfg->sections[curr_section]->name,
-				 sizeof(cfg->sections[0]->name), "GLOBAL");
+		snprintf(cfg->sections[curr_section].name,
+				 sizeof(cfg->sections[0].name), "GLOBAL");
 	}
 
 	while (fgets(buffer, sizeof(buffer), f) != NULL) {
@@ -213,7 +229,7 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 
 			/* close off old section and add start new one */
 			if (curr_section >= 0)
-				cfg->sections[curr_section]->num_entries =
+				cfg->sections[curr_section].num_entries =
 					curr_entry + 1;
 			curr_section++;
 
@@ -235,17 +251,17 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 			/* allocate space for new section */
 			allocated_entries = CFG_ALLOC_ENTRY_BATCH;
 			curr_entry = -1;
-			cfg->sections[curr_section] = malloc(
-				sizeof(*cfg->sections[0]) +
-				sizeof(cfg->sections[0]->entries[0]) *
+			cfg->sections = malloc(
+				sizeof(cfg->sections[0]) +
+				sizeof(cfg->sections[0].entries) *
 				allocated_entries);
-			if (cfg->sections[curr_section] == NULL) {
+			if (cfg->sections == NULL) {
 				printf("Error - no more memory\n");
 				goto error1;
 			}
 
-			snprintf(cfg->sections[curr_section]->name,
-					sizeof(cfg->sections[0]->name),
+			snprintf(cfg->sections[curr_section].name,
+					sizeof(cfg->sections[0].name),
 					"%s", &buffer[1]);
 		} else {
 			/* value line */
@@ -255,8 +271,7 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 				goto error1;
 			}
 
-			struct rte_cfgfile_section *sect =
-				cfg->sections[curr_section];
+			struct rte_cfgfile_section *sect = cfg->sections;
 
 			char *split[2] = {NULL};
 			split[0] = buffer;
@@ -292,18 +307,17 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 					printf("Error - no more memory\n");
 					goto error1;
 				}
-				sect = cfg->sections[curr_section] = n_sect;
+				sect = cfg->sections = n_sect;
 			}
 
-			sect->entries[curr_entry] = malloc(
-				sizeof(*sect->entries[0]));
-			if (sect->entries[curr_entry] == NULL) {
+			sect->entries = malloc(
+				sizeof(sect->entries[0]));
+			if (sect->entries == NULL) {
 				printf("Error - no more memory\n");
 				goto error1;
 			}
 
-			struct rte_cfgfile_entry *entry = sect->entries[
-				curr_entry];
+			struct rte_cfgfile_entry *entry = sect->entries;
 			snprintf(entry->name, sizeof(entry->name), "%s",
 				split[0]);
 			snprintf(entry->value, sizeof(entry->value), "%s",
@@ -319,42 +333,38 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 	cfg->num_sections = curr_section + 1;
 	/* curr_section will still be -1 if we have an empty file */
 	if (curr_section >= 0)
-		cfg->sections[curr_section]->num_entries = curr_entry + 1;
+		cfg->sections[curr_section].num_entries = curr_entry + 1;
 	return cfg;
 
 error1:
 	cfg->num_sections = curr_section + 1;
 	if (curr_section >= 0)
-		cfg->sections[curr_section]->num_entries = curr_entry + 1;
+		cfg->sections[curr_section].num_entries = curr_entry + 1;
 	rte_cfgfile_close(cfg);
 error2:
 	fclose(f);
 	return NULL;
 }
 
-
 int rte_cfgfile_close(struct rte_cfgfile *cfg)
 {
-	int i, j;
+	int i;
 
 	if (cfg == NULL)
 		return -1;
 
-	for (i = 0; i < cfg->num_sections; i++) {
-		if (cfg->sections[i] != NULL) {
-			if (cfg->sections[i]->num_entries) {
-				for (j = 0; j < cfg->sections[i]->num_entries;
-					j++) {
-					if (cfg->sections[i]->entries[j] !=
-						NULL)
-						free(cfg->sections[i]->
-							entries[j]);
-				}
+	if (cfg->sections != NULL) {
+		for (i = 0; i < cfg->allocated_sections; i++) {
+			if (cfg->sections[i].entries != NULL) {
+				free(cfg->sections[i].entries);
+				cfg->sections[i].entries = NULL;
 			}
-			free(cfg->sections[i]);
 		}
+		free(cfg->sections);
+		cfg->sections = NULL;
 	}
 	free(cfg);
+	cfg = NULL;
 
 	return 0;
 }
@@ -366,7 +376,7 @@ size_t length)
 	int i;
 	int num_sections = 0;
 	for (i = 0; i < cfg->num_sections; i++) {
-		if (strncmp(cfg->sections[i]->name, sectionname, length) == 0)
+		if (strncmp(cfg->sections[i].name, sectionname, length) == 0)
 			num_sections++;
 	}
 	return num_sections;
@@ -380,23 +390,11 @@ rte_cfgfile_sections(struct rte_cfgfile *cfg, char *sections[],
 
 	for (i = 0; i < cfg->num_sections && i < max_sections; i++)
 		snprintf(sections[i], CFG_NAME_LEN, "%s",
-		cfg->sections[i]->name);
+		cfg->sections[i].name);
 
 	return i;
 }
 
-static const struct rte_cfgfile_section *
-_get_section(struct rte_cfgfile *cfg, const char *sectionname)
-{
-	int i;
-	for (i = 0; i < cfg->num_sections; i++) {
-		if (strncmp(cfg->sections[i]->name, sectionname,
-				sizeof(cfg->sections[0]->name)) == 0)
-			return cfg->sections[i];
-	}
-	return NULL;
-}
-
 int
 rte_cfgfile_has_section(struct rte_cfgfile *cfg, const char *sectionname)
 {
@@ -409,7 +407,7 @@ rte_cfgfile_section_num_entries(struct rte_cfgfile *cfg,
 {
 	const struct rte_cfgfile_section *s = _get_section(cfg, sectionname);
 	if (s == NULL)
-		return -1;
+		return -EINVAL;
 	return s->num_entries;
 }
 
@@ -421,9 +419,9 @@ rte_cfgfile_section_entries(struct rte_cfgfile *cfg, const char *sectionname,
 	int i;
 	const struct rte_cfgfile_section *sect = _get_section(cfg, sectionname);
 	if (sect == NULL)
-		return -1;
+		return -EINVAL;
 	for (i = 0; i < max_entries && i < sect->num_entries; i++)
-		entries[i] = *sect->entries[i];
+		entries[i] = sect->entries[i];
 	return i;
 }
 
@@ -436,12 +434,12 @@ rte_cfgfile_section_entries_by_index(struct rte_cfgfile *cfg, int index,
 	const struct rte_cfgfile_section *sect;
 
 	if (index < 0 || index >= cfg->num_sections)
-		return -1;
+		return -EINVAL;
 
-	sect = cfg->sections[index];
+	sect = &cfg->sections[index];
 	snprintf(sectionname, CFG_NAME_LEN, "%s", sect->name);
 	for (i = 0; i < max_entries && i < sect->num_entries; i++)
-		entries[i] = *sect->entries[i];
+		entries[i] = sect->entries[i];
 	return i;
 }
 
@@ -454,9 +452,9 @@ rte_cfgfile_get_entry(struct rte_cfgfile *cfg, const char *sectionname,
 	if (sect == NULL)
 		return NULL;
 	for (i = 0; i < sect->num_entries; i++)
-		if (strncmp(sect->entries[i]->name, entryname, CFG_NAME_LEN)
-			== 0)
-			return sect->entries[i]->value;
+		if (strncmp(sect->entries[i].name, entryname, CFG_NAME_LEN)
+									== 0)
+			return sect->entries[i].value;
 	return NULL;
 }
 
diff --git a/lib/librte_cfgfile/rte_cfgfile.h b/lib/librte_cfgfile/rte_cfgfile.h
index fa10d40..4702110 100644
--- a/lib/librte_cfgfile/rte_cfgfile.h
+++ b/lib/librte_cfgfile/rte_cfgfile.h
@@ -178,7 +178,7 @@ int rte_cfgfile_has_section(struct rte_cfgfile *cfg, const char *sectionname);
 * @param sectionname
 *   Section name
 * @return
-*   Number of entries in section on success, -1 otherwise
+*   Number of entries in section on success, -EINVAL otherwise
 */
 int rte_cfgfile_section_num_entries(struct rte_cfgfile *cfg,
 	const char *sectionname);
@@ -199,7 +199,7 @@ int rte_cfgfile_section_num_entries(struct rte_cfgfile *cfg,
 * @param max_entries
 *   Maximum number of section entries to be stored in entries array
 * @return
-*   Number of entries populated on success, -1 otherwise
+*   Number of entries populated on success, -EINVAL otherwise
 */
 int rte_cfgfile_section_entries(struct rte_cfgfile *cfg,
 	const char *sectionname,
@@ -226,7 +226,7 @@ int rte_cfgfile_section_entries(struct rte_cfgfile *cfg,
 * @param max_entries
 *   Maximum number of section entries to be stored in entries array
 * @return
-*   Number of entries populated on success, -1 otherwise
+*   Number of entries populated on success, -EINVAL otherwise
 */
 int rte_cfgfile_section_entries_by_index(struct rte_cfgfile *cfg,
 	int index,
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v4 3/5] cfgfile: add new functions to API
  2017-07-10 12:44           ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Jacek Piasecki
  2017-07-10 12:44             ` [PATCH v4 1/5] cfgfile: remove EAL dependency Jacek Piasecki
  2017-07-10 12:44             ` [PATCH v4 2/5] cfgfile: change existing API functions Jacek Piasecki
@ 2017-07-10 12:44             ` Jacek Piasecki
  2017-08-30 20:18               ` Bruce Richardson
  2017-07-10 12:44             ` [PATCH v4 4/5] cfgfile: rework of load function Jacek Piasecki
                               ` (2 subsequent siblings)
  5 siblings, 1 reply; 70+ messages in thread
From: Jacek Piasecki @ 2017-07-10 12:44 UTC (permalink / raw)
  To: dev
  Cc: bruce.richardson, deepak.k.jain, kubax.kozak,
	michalx.k.jastrzebski, Jacek Piasecki

Extend existing cfgfile library with providing new API functions:

rte_cfgfile_create() - create new cfgfile object
rte_cfgfile_add_section() - add new section to existing cfgfile
object
rte_cfgfile_add_entry() - add new entry to existing cfgfile
object in specified section
rte_cfgfile_set_entry() - update existing entry in cfgfile object
rte_cfgfile_save() - save existing cfgfile object to INI file

This modification allows to create a cfgfile on
runtime and opens up the possibility to have applications
dynamically build up a proper DPDK configuration, rather than having
to have a pre-existing one.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 lib/librte_cfgfile/rte_cfgfile.c           | 186 +++++++++++++++++++++++++++++
 lib/librte_cfgfile/rte_cfgfile.h           |  76 ++++++++++++
 lib/librte_cfgfile/rte_cfgfile_version.map |  11 ++
 3 files changed, 273 insertions(+)

diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
index 50fe37a..225e42a 100644
--- a/lib/librte_cfgfile/rte_cfgfile.c
+++ b/lib/librte_cfgfile/rte_cfgfile.c
@@ -121,6 +121,36 @@ _get_section(struct rte_cfgfile *cfg, const char *sectionname)
 }
 
 static int
+_add_entry(struct rte_cfgfile_section *section, const char *entryname,
+		const char *entryvalue)
+{
+	/* resize entry structure if we don't have room for more entries */
+	if (section->num_entries == section->allocated_entries) {
+		struct rte_cfgfile_entry *n_entries = realloc(
+				section->entries,
+				sizeof(struct rte_cfgfile_entry) *
+				((section->allocated_entries) +
+						CFG_ALLOC_ENTRY_BATCH));
+
+		if (n_entries == NULL)
+			return -ENOMEM;
+
+		section->entries = n_entries;
+		section->allocated_entries += CFG_ALLOC_ENTRY_BATCH;
+	}
+	/* fill up entry fields with key name and value */
+	struct rte_cfgfile_entry *curr_entry =
+					&section->entries[section->num_entries];
+
+	snprintf(curr_entry->name, sizeof(curr_entry->name), "%s", entryname);
+	snprintf(curr_entry->value,
+				sizeof(curr_entry->value), "%s", entryvalue);
+	section->num_entries++;
+
+	return 0;
+}
+
+static int
 rte_cfgfile_check_params(const struct rte_cfgfile_parameters *params)
 {
 	unsigned int valid_comment;
@@ -346,6 +376,162 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 	return NULL;
 }
 
+struct rte_cfgfile *
+rte_cfgfile_create(int flags)
+{
+	int i;
+	struct rte_cfgfile *cfg = NULL;
+
+	cfg = malloc(sizeof(*cfg));
+
+	if (cfg == NULL)
+		return NULL;
+
+	cfg->flags = flags;
+	cfg->num_sections = 0;
+
+	/* allocate first batch of sections and entries */
+	cfg->sections = malloc(sizeof(struct rte_cfgfile_section) *
+			CFG_ALLOC_SECTION_BATCH);
+
+	if (cfg->sections == NULL)
+		return NULL;
+
+	cfg->allocated_sections = CFG_ALLOC_SECTION_BATCH;
+
+	for (i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) {
+		cfg->sections[i].entries = malloc(sizeof(
+			struct rte_cfgfile_entry) * CFG_ALLOC_ENTRY_BATCH);
+
+		if (cfg->sections[i].entries == NULL)
+			return NULL;
+
+		cfg->sections[i].num_entries = 0;
+		cfg->sections[i].allocated_entries = CFG_ALLOC_ENTRY_BATCH;
+	}
+
+	if (flags & CFG_FLAG_GLOBAL_SECTION)
+		rte_cfgfile_add_section(cfg, "GLOBAL");
+	return cfg;
+}
+
+int
+rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname)
+{
+	int i;
+
+	if (cfg == NULL)
+		return -EINVAL;
+
+	if (sectionname == NULL)
+		return -EINVAL;
+
+	/* resize overall struct if we don't have room for more	sections */
+	if (cfg->num_sections == cfg->allocated_sections) {
+
+		struct rte_cfgfile_section *n_sections =
+				realloc(cfg->sections,
+				sizeof(struct rte_cfgfile_section) *
+				((cfg->allocated_sections) +
+				CFG_ALLOC_SECTION_BATCH));
+
+		if (n_sections == NULL)
+			return -ENOMEM;
+
+		for (i = 0; i < CFG_ALLOC_SECTION_BATCH; i++) {
+			n_sections[i + cfg->allocated_sections].num_entries = 0;
+			n_sections[i +
+				 cfg->allocated_sections].allocated_entries = 0;
+			n_sections[i + cfg->allocated_sections].entries = NULL;
+		}
+		cfg->sections = n_sections;
+		cfg->allocated_sections += CFG_ALLOC_SECTION_BATCH;
+	}
+
+	snprintf(cfg->sections[cfg->num_sections].name,
+			sizeof(cfg->sections[0].name), "%s", sectionname);
+	cfg->sections[cfg->num_sections].num_entries = 0;
+	cfg->num_sections++;
+
+	return 0;
+}
+
+int rte_cfgfile_add_entry(struct rte_cfgfile *cfg,
+		const char *sectionname, const char *entryname,
+		const char *entryvalue)
+{
+	int ret;
+
+	if ((cfg == NULL) || (sectionname == NULL) || (entryname == NULL)
+			|| (entryvalue == NULL))
+		return -EINVAL;
+
+	if (rte_cfgfile_has_entry(cfg, sectionname, entryname) != 0)
+		return -EEXIST;
+
+	/* search for section pointer by sectionname */
+	struct rte_cfgfile_section *curr_section = _get_section(cfg,
+								sectionname);
+	if (curr_section == NULL)
+		return -EINVAL;
+
+	ret = _add_entry(curr_section, entryname, entryvalue);
+
+	return ret;
+}
+
+int rte_cfgfile_set_entry(struct rte_cfgfile *cfg, const char *sectionname,
+		const char *entryname, const char *entryvalue)
+{
+	int i;
+
+	if ((cfg == NULL) || (sectionname == NULL) || (entryname == NULL))
+		return -EINVAL;
+
+	/* search for section pointer by sectionname */
+	struct rte_cfgfile_section *curr_section = _get_section(cfg,
+								sectionname);
+	if (curr_section == NULL)
+		return -EINVAL;
+
+	if (entryvalue == NULL)
+		entryvalue = "";
+
+	for (i = 0; i < curr_section->num_entries; i++)
+		if (!strcmp(curr_section->entries[i].name, entryname)) {
+			snprintf(curr_section->entries[i].value,
+					sizeof(curr_section->entries[i].value),
+							"%s", entryvalue);
+			return 0;
+		}
+	printf("Error - entry name doesn't exist\n");
+	return -EINVAL;
+}
+
+int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename)
+{
+	int i, j;
+
+	if ((cfg == NULL) || (filename == NULL))
+		return -EINVAL;
+
+	FILE *f = fopen(filename, "w");
+
+	if (f == NULL)
+		return -EINVAL;
+
+	for (i = 0; i < cfg->num_sections; i++) {
+		fprintf(f, "[%s]\n", cfg->sections[i].name);
+
+		for (j = 0; j < cfg->sections[i].num_entries; j++) {
+			fprintf(f, "%s=%s\n",
+					cfg->sections[i].entries[j].name,
+					cfg->sections[i].entries[j].value);
+		}
+	}
+	return fclose(f);
+}
+
 int rte_cfgfile_close(struct rte_cfgfile *cfg)
 {
 	int i;
diff --git a/lib/librte_cfgfile/rte_cfgfile.h b/lib/librte_cfgfile/rte_cfgfile.h
index 4702110..8431b86 100644
--- a/lib/librte_cfgfile/rte_cfgfile.h
+++ b/lib/librte_cfgfile/rte_cfgfile.h
@@ -121,6 +121,82 @@ struct rte_cfgfile *rte_cfgfile_load_with_params(const char *filename,
 	int flags, const struct rte_cfgfile_parameters *params);
 
 /**
+ * Create new cfgfile instance with empty sections and entries
+ *
+ * @param flags
+ *   - CFG_FLAG_GLOBAL_SECTION
+ *     Indicates that the file supports key value entries before the first
+ *     defined section.  These entries can be accessed in the "GLOBAL"
+ *     section.
+ *   - CFG_FLAG_EMPTY_VALUES
+ *     Indicates that file supports key value entries where the value can
+ *     be zero length (e.g., "key=").
+ * @return
+ *   Handle to cfgfile instance on success, NULL otherwise
+ */
+struct rte_cfgfile *rte_cfgfile_create(int flags);
+
+/**
+ * Add section in cfgfile instance.
+ *
+ * @param cfg
+ *   Pointer to the cfgfile structure.
+ * @param sectionname
+ *   Section name which will be add to cfgfile.
+ * @return
+ *   0 on success, -ENOMEM if can't add section
+ */
+int
+rte_cfgfile_add_section(struct rte_cfgfile *cfg, const char *sectionname);
+
+/**
+ * Add entry to specified section in cfgfile instance.
+ *
+ * @param cfg
+ *   Pointer to the cfgfile structure.
+ * @param sectionname
+ *   Given section name to add an entry.
+ * @param entryname
+ *   Entry name to add.
+ * @param entryvalue
+ *   Entry value to add.
+ * @return
+ *   0 on success, -EEXIST if entry already exist, -EINVAL if bad argument
+ */
+int rte_cfgfile_add_entry(struct rte_cfgfile *cfg,
+		const char *sectionname, const char *entryname,
+		const char *entryvalue);
+
+/**
+ * Update value of specified entry name in given section in config file
+ *
+ * @param cfg
+ *   Config file
+ * @param sectionname
+ *   Section name
+ * @param entryname
+ *   Entry name to look for the value change
+ * @param entryvalue
+ *   New entry value. Can be also an empty string if CFG_FLAG_EMPTY_VALUES = 1
+ * @return
+ *   0 on success, -EINVAL if bad argument
+ */
+int rte_cfgfile_set_entry(struct rte_cfgfile *cfg, const char *sectionname,
+		const char *entryname, const char *entryvalue);
+
+/**
+ * Save object cfgfile to file on disc
+ *
+ * @param cfg
+ *   Config file structure
+ * @param filename
+ *   File name to save data
+ * @return
+ *   0 on success, errno otherwise
+ */
+int rte_cfgfile_save(struct rte_cfgfile *cfg, const char *filename);
+
+/**
 * Get number of sections in config file
 *
 * @param cfg
diff --git a/lib/librte_cfgfile/rte_cfgfile_version.map b/lib/librte_cfgfile/rte_cfgfile_version.map
index 5fe60f7..de68ff6 100644
--- a/lib/librte_cfgfile/rte_cfgfile_version.map
+++ b/lib/librte_cfgfile/rte_cfgfile_version.map
@@ -27,3 +27,14 @@ DPDK_17.05 {
     rte_cfgfile_load_with_params;
 
 } DPDK_16.04;
+
+DPDK_17.08 {
+	global:
+
+	rte_cfgfile_add_entry;
+	rte_cfgfile_add_section;
+	rte_cfgfile_create;
+	rte_cfgfile_save;
+	rte_cfgfile_set_entry;
+
+} DPDK_17.05;
\ No newline at end of file
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v4 4/5] cfgfile: rework of load function
  2017-07-10 12:44           ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Jacek Piasecki
                               ` (2 preceding siblings ...)
  2017-07-10 12:44             ` [PATCH v4 3/5] cfgfile: add new functions to API Jacek Piasecki
@ 2017-07-10 12:44             ` Jacek Piasecki
  2017-08-30 20:24               ` Bruce Richardson
  2017-07-10 12:44             ` [PATCH v4 5/5] test/cfgfile: add new unit test Jacek Piasecki
  2017-07-10 15:13             ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Thomas Monjalon
  5 siblings, 1 reply; 70+ messages in thread
From: Jacek Piasecki @ 2017-07-10 12:44 UTC (permalink / raw)
  To: dev
  Cc: bruce.richardson, deepak.k.jain, kubax.kozak,
	michalx.k.jastrzebski, Jacek Piasecki

New functions added to cfgfile library make it possible
to significantly simplify the code of rte_cfgfile_load_with_params()

This patch shows the new body of this function.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 lib/librte_cfgfile/rte_cfgfile.c | 156 ++++++++-------------------------------
 1 file changed, 29 insertions(+), 127 deletions(-)

diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
index 225e42a..e2e7307 100644
--- a/lib/librte_cfgfile/rte_cfgfile.c
+++ b/lib/librte_cfgfile/rte_cfgfile.c
@@ -189,10 +189,6 @@ struct rte_cfgfile *
 rte_cfgfile_load_with_params(const char *filename, int flags,
 			     const struct rte_cfgfile_parameters *params)
 {
-	int allocated_sections = CFG_ALLOC_SECTION_BATCH;
-	int allocated_entries = 0;
-	int curr_section = -1;
-	int curr_entry = -1;
 	char buffer[CFG_NAME_LEN + CFG_VALUE_LEN + 4] = {0};
 	int lineno = 0;
 	struct rte_cfgfile *cfg = NULL;
@@ -204,28 +200,7 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 	if (f == NULL)
 		return NULL;
 
-	cfg = malloc(sizeof(*cfg) + sizeof(cfg->sections[0]) *
-		allocated_sections);
-	if (cfg == NULL)
-		goto error2;
-
-	memset(cfg->sections, 0, sizeof(cfg->sections[0]) * allocated_sections);
-
-	if (flags & CFG_FLAG_GLOBAL_SECTION) {
-		curr_section = 0;
-		allocated_entries = CFG_ALLOC_ENTRY_BATCH;
-		cfg->sections = malloc(
-			sizeof(cfg->sections[0]) +
-			sizeof(cfg->sections[0].entries) *
-			allocated_entries);
-		if (cfg->sections == NULL) {
-			printf("Error - no memory for global section\n");
-			goto error1;
-		}
-
-		snprintf(cfg->sections[curr_section].name,
-				 sizeof(cfg->sections[0].name), "GLOBAL");
-	}
+	cfg = rte_cfgfile_create(flags);
 
 	while (fgets(buffer, sizeof(buffer), f) != NULL) {
 		char *pos = NULL;
@@ -236,13 +211,15 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 					"Check if line too long\n", lineno);
 			goto error1;
 		}
+		/* skip parsing if comment character found */
 		pos = memchr(buffer, params->comment_character, len);
-		if (pos != NULL) {
+		if (pos != NULL && (*(pos-1) != '\\')) {
 			*pos = '\0';
 			len = pos -  buffer;
 		}
 
 		len = _strip(buffer, len);
+		/* skip lines without useful content */
 		if (buffer[0] != '[' && memchr(buffer, '=', len) == NULL)
 			continue;
 
@@ -250,128 +227,53 @@ rte_cfgfile_load_with_params(const char *filename, int flags,
 			/* section heading line */
 			char *end = memchr(buffer, ']', len);
 			if (end == NULL) {
-				printf("Error line %d - no terminating '['"
+				printf("Error line %d - no terminating ']'"
 					"character found\n", lineno);
 				goto error1;
 			}
 			*end = '\0';
 			_strip(&buffer[1], end - &buffer[1]);
 
-			/* close off old section and add start new one */
-			if (curr_section >= 0)
-				cfg->sections[curr_section].num_entries =
-					curr_entry + 1;
-			curr_section++;
-
-			/* resize overall struct if we don't have room for more
-			sections */
-			if (curr_section == allocated_sections) {
-				allocated_sections += CFG_ALLOC_SECTION_BATCH;
-				struct rte_cfgfile *n_cfg = realloc(cfg,
-					sizeof(*cfg) + sizeof(cfg->sections[0])
-					* allocated_sections);
-				if (n_cfg == NULL) {
-					curr_section--;
-					printf("Error - no more memory\n");
-					goto error1;
-				}
-				cfg = n_cfg;
-			}
-
-			/* allocate space for new section */
-			allocated_entries = CFG_ALLOC_ENTRY_BATCH;
-			curr_entry = -1;
-			cfg->sections = malloc(
-				sizeof(cfg->sections[0]) +
-				sizeof(cfg->sections[0].entries) *
-				allocated_entries);
-			if (cfg->sections == NULL) {
-				printf("Error - no more memory\n");
-				goto error1;
-			}
-
-			snprintf(cfg->sections[curr_section].name,
-					sizeof(cfg->sections[0].name),
-					"%s", &buffer[1]);
+			rte_cfgfile_add_section(cfg, &buffer[1]);
 		} else {
-			/* value line */
-			if (curr_section < 0) {
-				printf("Error line %d - value outside of"
-					"section\n", lineno);
-				goto error1;
-			}
-
-			struct rte_cfgfile_section *sect = cfg->sections;
-
+			/* key and value line */
 			char *split[2] = {NULL};
+
 			split[0] = buffer;
 			split[1] = memchr(buffer, '=', len);
-
-			/* when delimeter not found */
-			if (split[1] == NULL) {
-				printf("Error at line %d - cannot "
-					"split string\n", lineno);
-				goto error1;
-			} else {
-				/* when delimeter found */
-				*split[1] = '\0';
-				split[1]++;
-
-				if (!(flags & CFG_FLAG_EMPTY_VALUES) &&
-						(*split[1] == '\0')) {
-					printf("Error at line %d - cannot "
-						"split string\n", lineno);
-					goto error1;
-				}
+			*split[1] = '\0';
+			split[1]++;
+
+			_strip(split[0], strlen(split[0]));
+			_strip(split[1], strlen(split[1]));
+			char *end = memchr(split[1], '\\', strlen(split[1]));
+			while (end != NULL) {
+				if (*(end+1) == params->comment_character) {
+					*end = '\0';
+					strcat(split[1], end+1);
+				} else
+					end++;
+				end = memchr(end, '\\', strlen(end));
 			}
 
-			curr_entry++;
-			if (curr_entry == allocated_entries) {
-				allocated_entries += CFG_ALLOC_ENTRY_BATCH;
-				struct rte_cfgfile_section *n_sect = realloc(
-					sect, sizeof(*sect) +
-					sizeof(sect->entries[0]) *
-					allocated_entries);
-				if (n_sect == NULL) {
-					curr_entry--;
-					printf("Error - no more memory\n");
-					goto error1;
-				}
-				sect = cfg->sections = n_sect;
+			if (!(flags & CFG_FLAG_EMPTY_VALUES) &&
+					(*split[1] == '\0')) {
+				printf("Error at line %d - cannot use empty "
+							"values\n", lineno);
+				goto error1;
 			}
 
-			sect->entries = malloc(
-				sizeof(sect->entries[0]));
-			if (sect->entries == NULL) {
-				printf("Error - no more memory\n");
+			if (cfg->num_sections == 0)
 				goto error1;
-			}
 
-			struct rte_cfgfile_entry *entry = sect->entries;
-			snprintf(entry->name, sizeof(entry->name), "%s",
-				split[0]);
-			snprintf(entry->value, sizeof(entry->value), "%s",
-				 split[1] ? split[1] : "");
-			_strip(entry->name, strnlen(entry->name,
-				sizeof(entry->name)));
-			_strip(entry->value, strnlen(entry->value,
-				sizeof(entry->value)));
+			_add_entry(&cfg->sections[cfg->num_sections - 1],
+					split[0], (split[1] ? split[1] : ""));
 		}
 	}
 	fclose(f);
-	cfg->flags = flags;
-	cfg->num_sections = curr_section + 1;
-	/* curr_section will still be -1 if we have an empty file */
-	if (curr_section >= 0)
-		cfg->sections[curr_section].num_entries = curr_entry + 1;
 	return cfg;
-
 error1:
-	cfg->num_sections = curr_section + 1;
-	if (curr_section >= 0)
-		cfg->sections[curr_section].num_entries = curr_entry + 1;
 	rte_cfgfile_close(cfg);
-error2:
 	fclose(f);
 	return NULL;
 }
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v4 5/5] test/cfgfile: add new unit test
  2017-07-10 12:44           ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Jacek Piasecki
                               ` (3 preceding siblings ...)
  2017-07-10 12:44             ` [PATCH v4 4/5] cfgfile: rework of load function Jacek Piasecki
@ 2017-07-10 12:44             ` Jacek Piasecki
  2017-08-30 20:25               ` Bruce Richardson
  2017-09-04  9:30               ` Bruce Richardson
  2017-07-10 15:13             ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Thomas Monjalon
  5 siblings, 2 replies; 70+ messages in thread
From: Jacek Piasecki @ 2017-07-10 12:44 UTC (permalink / raw)
  To: dev
  Cc: bruce.richardson, deepak.k.jain, kubax.kozak,
	michalx.k.jastrzebski, Jacek Piasecki

Load huge realloc_sections.ini file to check malloc/realloc
ability of cfgfile library.

Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
---
 test/test/test_cfgfile.c                         |  40 +++++++
 test/test/test_cfgfiles/etc/realloc_sections.ini | 128 +++++++++++++++++++++++
 2 files changed, 168 insertions(+)
 create mode 100644 test/test/test_cfgfiles/etc/realloc_sections.ini

diff --git a/test/test/test_cfgfile.c b/test/test/test_cfgfile.c
index 4cc9b14..2278618 100644
--- a/test/test/test_cfgfile.c
+++ b/test/test/test_cfgfile.c
@@ -111,6 +111,7 @@ _test_cfgfile_sample(struct rte_cfgfile *cfgfile)
 	return 0;
 }
 
+
 static int
 test_cfgfile_sample1(void)
 {
@@ -154,6 +155,42 @@ test_cfgfile_sample2(void)
 }
 
 static int
+test_cfgfile_realloc_sections(void)
+{
+	struct rte_cfgfile *cfgfile;
+	int ret;
+	const char *value;
+
+	cfgfile = rte_cfgfile_load(CFG_FILES_ETC "/realloc_sections.ini", 0);
+	TEST_ASSERT_NOT_NULL(cfgfile, "Failed to load config file");
+
+	ret = rte_cfgfile_num_sections(cfgfile, NULL, 0);
+	TEST_ASSERT(ret == 9, "Unexpected number of sections: %d", ret);
+
+	ret = rte_cfgfile_has_section(cfgfile, "section9");
+	TEST_ASSERT(ret, "section9 missing");
+
+	ret = rte_cfgfile_section_num_entries(cfgfile, "section3");
+	TEST_ASSERT(ret == 21,
+			"section3 unexpected number of entries: %d", ret);
+
+	ret = rte_cfgfile_section_num_entries(cfgfile, "section9");
+	TEST_ASSERT(ret == 8, "section9 unexpected number of entries: %d", ret);
+
+	value = rte_cfgfile_get_entry(cfgfile, "section9", "key8");
+	TEST_ASSERT(strcmp("value8_section9", value) == 0,
+		    "key unexpected value: %s", value);
+
+	ret = rte_cfgfile_save(cfgfile, "cfgfile_save.ini");
+	TEST_ASSERT_SUCCESS(ret, "Failed to save *.ini file");
+
+	ret = rte_cfgfile_close(cfgfile);
+	TEST_ASSERT_SUCCESS(ret, "Failed to close cfgfile");
+
+	return 0;
+}
+
+static int
 test_cfgfile_invalid_section_header(void)
 {
 	struct rte_cfgfile *cfgfile;
@@ -292,6 +329,9 @@ test_cfgfile(void)
 	if (test_cfgfile_sample2())
 		return -1;
 
+	if (test_cfgfile_realloc_sections())
+		return -1;
+
 	if (test_cfgfile_invalid_section_header())
 		return -1;
 
diff --git a/test/test/test_cfgfiles/etc/realloc_sections.ini b/test/test/test_cfgfiles/etc/realloc_sections.ini
new file mode 100644
index 0000000..e653e40
--- /dev/null
+++ b/test/test/test_cfgfiles/etc/realloc_sections.ini
@@ -0,0 +1,128 @@
+[section1]
+key1=value1_section1
+key2=value2_section1
+key3=value3_section1
+key4=value4_section1
+key5=value5_section1
+key6=value6_section1
+key7=value7_section1
+key8=value8_section1
+key9=value9_section1
+key10=value10_section1
+key11=value11_section1
+key12=value12_section1
+key13=value13_section1
+key14=value14_section1
+key15=value15_section1
+key16=value16_section1
+key17=value17_section1
+key18=value18_section1
+key19=value19_section1
+key20=value20_section1
+key21=value21_section1
+
+[section2]
+key1=value1_section2
+key2=value2_section2
+key3=value3_section2
+key4=value4_section2
+key5=value5_section2
+key6=value6_section2
+key7=value7_section2
+key8=value8_section2
+key9=value9_section2
+key10=value10_section2
+key11=value11_section2
+key12=value12_section2
+key13=value13_section2
+key14=value14_section2
+key15=value15_section2
+key16=value16_section2
+key17=value17_section2
+key18=value18_section2
+key19=value19_section2
+key20=value20_section2
+key21=value21_section2
+
+[section3]
+key1=value1_section3
+key2=value2_section3
+key3=value3_section3
+key4=value4_section3
+key5=value5_section3
+key6=value6_section3
+key7=value7_section3
+key8=value8_section3
+key9=value9_section3
+key10=value10_section3
+key11=value11_section3
+key12=value12_section3
+key13=value13_section3
+key14=value14_section3
+key15=value15_section3
+key16=value16_section3
+key17=value17_section3
+key18=value18_section3
+key19=value19_section3
+key20=value20_section3
+key21=value21_section3
+
+[section4]
+key1=value1_section4
+key2=value2_section4
+key3=value3_section4
+key4=value4_section4
+key5=value5_section4
+key6=value6_section4
+key7=value7_section4
+key8=value8_section4
+
+[section5]
+key1=value1_section5
+key2=value2_section5
+key3=value3_section5
+key4=value4_section5
+key5=value5_section5
+key6=value6_section5
+key7=value7_section5
+key8=value8_section5
+
+[section6]
+key1=value1_section6
+key2=value2_section6
+key3=value3_section6
+key4=value4_section6
+key5=value5_section6
+key6=value6_section6
+key7=value7_section6
+key8=value8_section6
+
+[section7]
+key1=value1_section7
+key2=value2_section7
+key3=value3_section7
+key4=value4_section7
+key5=value5_section7
+key6=value6_section7
+key7=value7_section7
+key8=value8_section7
+
+[section8]
+key1=value1_section8
+key2=value2_section8
+key3=value3_section8
+key4=value4_section8
+key5=value5_section8
+key6=value6_section8
+key7=value7_section8
+key8=value8_section8
+
+[section9]
+key1=value1_section9
+key2=value2_section9
+key3=value3_section9
+key4=value4_section9
+key5=value5_section9
+key6=value6_section9
+key7=value7_section9
+key8=value8_section9
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v4 0/3] EAL change for using a config file for DPDK
  2017-06-27 10:52         ` [PATCH v3 1/3] eal: add functions parsing EAL arguments Jacek Piasecki
  2017-06-30 16:04           ` Bruce Richardson
  2017-07-10 12:44           ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Jacek Piasecki
@ 2017-07-10 12:51           ` Kuba Kozak
  2017-07-10 12:51             ` [PATCH v4 1/3] eal: add functions parsing EAL arguments Kuba Kozak
                               ` (2 more replies)
  2 siblings, 3 replies; 70+ messages in thread
From: Kuba Kozak @ 2017-07-10 12:51 UTC (permalink / raw)
  To: dev
  Cc: deepak.k.jain, bruce.richardson, michalx.k.jastrzebski,
	jacekx.piasecki, Kuba Kozak

This patchset introduce a mechanism for running dpdk application with 
parameters provided by configuration file.

A new API for EAL takes a config file data type - either loaded from
file, or built up programmatically in the application - and extracts
DPDK parameters from it to be used when eal init is called. 
This allows apps to have an alternative method to configure EAL,
other than via command-line parameters.

Reworked applications are used to demonstrate the new eal API.
If a --cfgfile-path <path> option is passed into command line non
EAL section, then the file is loaded and used by app. If a file
called config.ini is present in current working directory, and
no --cfgfile-path option is passed in, config.ini file will be
loaded and used by app.

Patch "app/testpmd: add parse options from JSON cfg file" 
demonstrates the usage of JSON instead of INI file format. 
JSON file can be called the same way as above, 
through --cfgfile-path <path> argument.
---
this patch depends on:
"Rework cfgfile API to enable apps config file support"

v4:
 Code optimalisation in parse_vdev_devices() function.
 Moved some functions from librte_eal/bsdapp and librte_eal/linuxapp
 to the librte_eal/common.
 Bug fixes.

v3: 
 split one patchset into two distinct patchsets:
 1. cfgfile library and TEST app changes
 2. EAL changes and examples (this patchset depends on cfgfile)

v2:
  lib eal:
	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
	Now this function load data from cfg structure and did initial
	initialization of EAL arguments. Vdev argument are stored in different
	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
	function it is necessary to call rte_eal_init to complete EAL
	initialization. There is no more merging arguments from different
	sources (cfg file and command line).
  	Added non_eal_configure to testpmd application.
	Function maintain the same functionality as rte_eal_configure but
	for non-eal arguments. 
  	Added config JSON feature to testpmd last patch from patchset contain
	example showing use of .json configuration files.

  lib cfgfile:
  	Rework of add_section(), add_entry() new implementation
  	New members allocated_entries/sections, free_entries/sections
	in rte_cfgfile structure, change in array of pointers
	**sections, **entries instead of *sections[], *entries[]
  	Add  set_entry() to update/overwrite already existing entry in cfgfile
	struct
  	Add save() function to save on disc cfgfile structure in INI format
  	Rework of existing load() function  simplifying the code
  	Add unit test realloc_sections() in TEST app for testing realloc/malloc
	of new API functions, add test for save() function

Kuba Kozak (3):
  eal: add functions parsing EAL arguments
  app/testpmd: add parse options from cfg file
  app/testpmd: add parse options from JSON cfg file

 app/test-pmd/Makefile                           |    6 +
 app/test-pmd/config.ini                         |   24 +
 app/test-pmd/config.json                        |   33 +
 app/test-pmd/parameters.c                       | 1181 +++++++++++++----------
 app/test-pmd/testpmd.c                          |  159 ++-
 app/test-pmd/testpmd.h                          |    3 +-
 config/common_base                              |    5 +
 lib/Makefile                                    |    3 +
 lib/librte_eal/bsdapp/eal/Makefile              |    4 +
 lib/librte_eal/bsdapp/eal/eal.c                 |  165 +++-
 lib/librte_eal/bsdapp/eal/rte_eal_version.map   |    1 +
 lib/librte_eal/common/eal_common_lcore.c        |    7 +
 lib/librte_eal/common/eal_common_options.c      |  106 ++
 lib/librte_eal/common/eal_options.h             |    3 +
 lib/librte_eal/common/include/rte_eal.h         |   20 +
 lib/librte_eal/linuxapp/eal/Makefile            |    3 +
 lib/librte_eal/linuxapp/eal/eal.c               |  265 +++--
 lib/librte_eal/linuxapp/eal/rte_eal_version.map |    1 +
 mk/rte.app.mk                                   |    2 +-
 19 files changed, 1349 insertions(+), 642 deletions(-)
 create mode 100644 app/test-pmd/config.ini
 create mode 100644 app/test-pmd/config.json

-- 
2.7.4

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v4 1/3] eal: add functions parsing EAL arguments
  2017-07-10 12:51           ` [PATCH v4 0/3] EAL change for using a config file for DPDK Kuba Kozak
@ 2017-07-10 12:51             ` Kuba Kozak
  2017-07-13  9:26               ` [PATCH v5 0/3] EAL change for using a config file for DPDK Kuba Kozak
  2017-07-13 10:07               ` Kuba Kozak
  2017-07-10 12:51             ` [PATCH v4 2/3] app/testpmd: add parse options from cfg file Kuba Kozak
  2017-07-10 12:51             ` [PATCH v4 3/3] app/testpmd: add parse options from JSON " Kuba Kozak
  2 siblings, 2 replies; 70+ messages in thread
From: Kuba Kozak @ 2017-07-10 12:51 UTC (permalink / raw)
  To: dev
  Cc: deepak.k.jain, bruce.richardson, michalx.k.jastrzebski,
	jacekx.piasecki, Kuba Kozak

added function rte_eal_configure which configure
Environment Abstraction Layer (EAL) using
configuration structure.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 lib/Makefile                                    |   3 +
 lib/librte_eal/bsdapp/eal/Makefile              |   4 +
 lib/librte_eal/bsdapp/eal/eal.c                 | 165 +++++++++++----
 lib/librte_eal/bsdapp/eal/rte_eal_version.map   |   1 +
 lib/librte_eal/common/eal_common_lcore.c        |   7 +
 lib/librte_eal/common/eal_common_options.c      | 106 ++++++++++
 lib/librte_eal/common/eal_options.h             |   3 +
 lib/librte_eal/common/include/rte_eal.h         |  20 ++
 lib/librte_eal/linuxapp/eal/Makefile            |   3 +
 lib/librte_eal/linuxapp/eal/eal.c               | 265 ++++++++++++++++--------
 lib/librte_eal/linuxapp/eal/rte_eal_version.map |   1 +
 mk/rte.app.mk                                   |   2 +-
 12 files changed, 452 insertions(+), 128 deletions(-)

diff --git a/lib/Makefile b/lib/Makefile
index 1080a95..2c6c380 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -34,6 +34,9 @@ include $(RTE_SDK)/mk/rte.vars.mk
 DIRS-y += librte_compat
 DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile
 DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+DEPDIRS-librte_eal := librte_cfgfile
+endif
 DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
 DEPDIRS-librte_ring := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index a0f9950..d70eefb 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -50,6 +50,10 @@ EXPORT_MAP := rte_eal_version.map
 
 LIBABIVER := 4
 
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -lrte_cfgfile
+endif
+
 # specific to bsdapp exec-env
 SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) := eal.c
 SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += eal_memory.c
diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index 05f0c1f..4a0c221 100644
--- a/lib/librte_eal/bsdapp/eal/eal.c
+++ b/lib/librte_eal/bsdapp/eal/eal.c
@@ -73,6 +73,7 @@
 #include <rte_version.h>
 #include <rte_atomic.h>
 #include <malloc_heap.h>
+#include <rte_cfgfile.h>
 
 #include "eal_private.h"
 #include "eal_thread.h"
@@ -99,6 +100,8 @@ static struct flock wr_lock = {
 		.l_len = sizeof(early_mem_config.memseg),
 };
 
+static int run_once_reset_internal_config;
+
 /* Address of global and public configuration */
 static struct rte_config rte_config = {
 		.mem_config = &early_mem_config,
@@ -347,6 +350,58 @@ eal_log_level_parse(int argc, char **argv)
 	optarg = old_optarg;
 }
 
+/* Parse single argument */
+static int
+eal_parse_option(int opt, char *optarg, int option_index, char *prgname)
+{
+	int ret;
+
+	/* getopt is not happy, stop right now */
+	if (opt == '?') {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+
+	ret = eal_parse_common_option(opt, optarg, &internal_config);
+	/* common parser is not happy */
+	if (ret < 0) {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+	/* common parser handled this option */
+	if (ret == 0)
+		return 0;
+
+	switch (opt) {
+	case 'h':
+		eal_usage(prgname);
+		exit(EXIT_SUCCESS);
+		break;
+
+	default:
+		if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
+			RTE_LOG(ERR, EAL, "Option %c is not supported "
+				"on FreeBSD\n", opt);
+		} else if (opt >= OPT_LONG_MIN_NUM &&
+			   opt < OPT_LONG_MAX_NUM) {
+			RTE_LOG(ERR, EAL, "Option %s is not supported "
+				"on FreeBSD\n",
+				eal_long_options[option_index].name);
+		} else {
+			RTE_LOG(ERR, EAL, "Option %d is not supported "
+				"on FreeBSD\n", opt);
+		}
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+	return 0;
+out:
+	return ret;
+}
+
 /* Parse the argument given in the command line of the application */
 static int
 eal_parse_args(int argc, char **argv)
@@ -367,45 +422,9 @@ eal_parse_args(int argc, char **argv)
 	while ((opt = getopt_long(argc, argvopt, eal_short_options,
 				  eal_long_options, &option_index)) != EOF) {
 
-		/* getopt is not happy, stop right now */
-		if (opt == '?') {
-			eal_usage(prgname);
-			ret = -1;
-			goto out;
-		}
-
-		ret = eal_parse_common_option(opt, optarg, &internal_config);
-		/* common parser is not happy */
-		if (ret < 0) {
-			eal_usage(prgname);
-			ret = -1;
-			goto out;
-		}
-		/* common parser handled this option */
-		if (ret == 0)
-			continue;
-
-		switch (opt) {
-		case 'h':
-			eal_usage(prgname);
-			exit(EXIT_SUCCESS);
-		default:
-			if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
-				RTE_LOG(ERR, EAL, "Option %c is not supported "
-					"on FreeBSD\n", opt);
-			} else if (opt >= OPT_LONG_MIN_NUM &&
-				   opt < OPT_LONG_MAX_NUM) {
-				RTE_LOG(ERR, EAL, "Option %s is not supported "
-					"on FreeBSD\n",
-					eal_long_options[option_index].name);
-			} else {
-				RTE_LOG(ERR, EAL, "Option %d is not supported "
-					"on FreeBSD\n", opt);
-			}
-			eal_usage(prgname);
-			ret = -1;
+		ret = eal_parse_option(opt, optarg, option_index, prgname);
+		if (ret < 0)
 			goto out;
-		}
 	}
 
 	if (eal_adjust_config(&internal_config) != 0) {
@@ -517,7 +536,10 @@ rte_eal_init(int argc, char **argv)
 
 	thread_id = pthread_self();
 
-	eal_reset_internal_config(&internal_config);
+	if (!run_once_reset_internal_config) {
+		eal_reset_internal_config(&internal_config);
+		run_once_reset_internal_config = 1;
+	}
 
 	/* set log level as early as possible */
 	eal_log_level_parse(argc, argv);
@@ -677,3 +699,68 @@ rte_eal_process_type(void)
 {
 	return rte_config.process_type;
 }
+
+#ifdef RTE_LIBRTE_CFGFILE
+int
+rte_eal_configure(struct rte_cfgfile *cfg, char *prgname)
+{
+	int n_entries;
+	int i;
+	int opt;
+	int option_index;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, "DPDK");
+
+	if (n_entries < 1) {
+		printf("No DPDK section entries in cfgfile object\n");
+		return 0;
+	}
+
+	struct rte_cfgfile_entry entries[n_entries];
+
+	if (n_entries !=
+			rte_cfgfile_section_entries(cfg, "DPDK", entries,
+					n_entries)) {
+		rte_eal_init_alert("Unexpected fault.");
+		rte_errno = EFAULT;
+		return -1;
+	}
+
+	if (!run_once_reset_internal_config) {
+		eal_reset_internal_config(&internal_config);
+		run_once_reset_internal_config = 1;
+	}
+
+	/* set log level as early as possible */
+	eal_log_level_cfg(cfg);
+
+	if (rte_eal_cpu_init() < 0) {
+		rte_eal_init_alert("Cannot detect lcores.");
+		rte_errno = ENOTSUP;
+		return -1;
+	}
+
+	for (i = 0; i < n_entries; i++) {
+		eal_getopt(entries[i].name, &opt, &option_index);
+
+		if (eal_parse_option(opt, entries[i].value,
+				option_index, prgname) != 0) {
+			rte_eal_init_alert("Invalid config file arguments.");
+			rte_errno = EINVAL;
+			return -1;
+		}
+	}
+
+	if (parse_vdev_devices(cfg) < 0) {
+		rte_eal_init_alert("Couldn't parse vdevs");
+		rte_errno = ENOMEM;
+		return -1;
+	}
+	return 0;
+}
+#endif
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 381f895..932f990 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -200,6 +200,7 @@ DPDK_17.08 {
 	rte_bus_find;
 	rte_bus_find_by_device;
 	rte_bus_find_by_name;
+	rte_eal_configure;
 
 } DPDK_17.05;
 
diff --git a/lib/librte_eal/common/eal_common_lcore.c b/lib/librte_eal/common/eal_common_lcore.c
index 84fa0cb..d0f4c6f 100644
--- a/lib/librte_eal/common/eal_common_lcore.c
+++ b/lib/librte_eal/common/eal_common_lcore.c
@@ -53,11 +53,18 @@
 int
 rte_eal_cpu_init(void)
 {
+	static int run_once;
+	static int ret;
 	/* pointer to global configuration */
 	struct rte_config *config = rte_eal_get_configuration();
 	unsigned lcore_id;
 	unsigned count = 0;
 
+	/* No need to calculate this function again if we know the result */
+	if (run_once)
+		return ret;
+	run_once = 1;
+
 	/*
 	 * Parse the maximum set of logical cores, detect the subset of running
 	 * ones and enable them by default.
diff --git a/lib/librte_eal/common/eal_common_options.c b/lib/librte_eal/common/eal_common_options.c
index 075b0ea..b568f05 100644
--- a/lib/librte_eal/common/eal_common_options.c
+++ b/lib/librte_eal/common/eal_common_options.c
@@ -50,6 +50,10 @@
 #include <rte_version.h>
 #include <rte_devargs.h>
 #include <rte_memcpy.h>
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#include <rte_errno.h>
+#endif
 
 #include "eal_internal_cfg.h"
 #include "eal_options.h"
@@ -1078,3 +1082,105 @@ eal_common_usage(void)
 	       "  --"OPT_NO_SHCONF"         No shared config (mmap'd files)\n"
 	       "\n", RTE_MAX_LCORE);
 }
+
+#ifdef RTE_LIBRTE_CFGFILE
+/* Parse the arguments for --log-level only */
+void
+eal_log_level_cfg(struct rte_cfgfile *cfg)
+{
+	const char *entry;
+
+	entry = rte_cfgfile_get_entry(cfg, "DPDK", OPT_LOG_LEVEL);
+	if (entry)
+		eal_parse_common_option(OPT_LOG_LEVEL_NUM, entry,
+				&internal_config);
+}
+#endif
+
+#ifdef RTE_LIBRTE_CFGFILE
+static void rte_eal_init_alert(const char *msg)
+{
+	fprintf(stderr, "EAL: FATAL: %s\n", msg);
+	RTE_LOG(ERR, EAL, "%s\n", msg);
+}
+
+#define vdev_buff_size		200
+#define sectionname_size	20
+int
+parse_vdev_devices(struct rte_cfgfile *cfg)
+{
+	char sectionname[sectionname_size] = "DPDK.vdev0";
+	char buffer[vdev_buff_size];
+	int vdev_nb = 0;
+	int n_entries;
+	int buf_len;
+	char *buf_ptr;
+	int i;
+	int ret;
+
+	/* ----------- parsing VDEVS */
+	for (vdev_nb = 1; rte_cfgfile_has_section(cfg, sectionname);
+			vdev_nb++) {
+		n_entries = rte_cfgfile_section_num_entries(cfg, sectionname);
+
+		struct rte_cfgfile_entry entries[n_entries];
+
+		if (n_entries != rte_cfgfile_section_entries(cfg, sectionname,
+				entries, n_entries)) {
+			rte_eal_init_alert("Unexpected fault.");
+			rte_errno = EFAULT;
+			return -1;
+		}
+
+		buffer[0] = '\0';
+		buf_ptr = buffer;
+		buf_len = vdev_buff_size;
+		for (i = 0; i < n_entries; i++) {
+			ret = snprintf(buf_ptr, buf_len, "%s%s%s%s",
+					entries[i].name,
+					(entries[i].value[0] != '\0') ?
+							"=" : "",
+					entries[i].value,
+					(i < (n_entries - 1)) ? "," : "");
+			if (ret >= buf_len) {
+				printf("parse_vdev_devices(): buffer[] size is "
+						"to small\n");
+				return -1;
+			}
+			buf_len -= ret;
+			buf_ptr += ret;
+		}
+
+		/* parsing vdev */
+		if (rte_eal_devargs_add(RTE_DEVTYPE_VIRTUAL,
+				buffer) < 0) {
+			return -1;
+		}
+		snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
+	}
+	/* ----------- parsing VDEVS */
+	return 0;
+}
+
+void
+eal_getopt(const char *str, int *opt, int *option_index)
+{
+	int i;
+
+	*opt = '?';
+	*option_index = 0;
+
+	if (strlen(str) == 1) {
+		*opt = *str;
+		return;
+	}
+
+	for (i = 0; eal_long_options[i].name != NULL; i++) {
+		if (strcmp(str, eal_long_options[i].name) == 0) {
+			*opt = eal_long_options[i].val;
+			*option_index = i;
+			break;
+		}
+	}
+}
+#endif
diff --git a/lib/librte_eal/common/eal_options.h b/lib/librte_eal/common/eal_options.h
index a881c62..0fce11c 100644
--- a/lib/librte_eal/common/eal_options.h
+++ b/lib/librte_eal/common/eal_options.h
@@ -96,5 +96,8 @@ int eal_check_common_options(struct internal_config *internal_cfg);
 void eal_common_usage(void);
 enum rte_proc_type_t eal_proc_type_detect(void);
 int eal_plugins_init(void);
+void eal_log_level_cfg(struct rte_cfgfile *cfg);
+int parse_vdev_devices(struct rte_cfgfile *cfg);
+void eal_getopt(const char *str, int *opt, int *option_index);
 
 #endif /* EAL_OPTIONS_H */
diff --git a/lib/librte_eal/common/include/rte_eal.h b/lib/librte_eal/common/include/rte_eal.h
index 6b7c5ca..a1c78c0 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -46,6 +46,8 @@
 #include <rte_per_lcore.h>
 #include <rte_config.h>
 
+struct rte_cfgfile; /* forward declaration of struct */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -188,6 +190,24 @@ int rte_eal_iopl_init(void);
  */
 int rte_eal_init(int argc, char **argv);
 
+#ifdef RTE_LIBRTE_CFGFILE
+/**
+ * Initialize the Environment Abstraction Layer (EAL) using
+ * configuration structure
+ *
+ * @param cfg
+ *   pointer to config file structure.
+ * @param prgname
+ *   pointer to string with execution path
+ *
+ * @return
+ *  - On success, return 0
+ *  - On failure, returns -1.
+ */
+int
+rte_eal_configure(struct rte_cfgfile *cfg, char *prgname);
+#endif
+
 /**
  * Check if a primary process is currently alive
  *
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 8651e27..7c1c559 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -53,6 +53,9 @@ LDLIBS += -lrt
 ifeq ($(CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES),y)
 LDLIBS += -lnuma
 endif
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -lrte_cfgfile
+endif
 
 # specific to linuxapp exec-env
 SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) := eal.c
diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
index 7c78f2d..f09aeff 100644
--- a/lib/librte_eal/linuxapp/eal/eal.c
+++ b/lib/librte_eal/linuxapp/eal/eal.c
@@ -78,6 +78,9 @@
 #include <rte_version.h>
 #include <rte_atomic.h>
 #include <malloc_heap.h>
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#endif
 
 #include "eal_private.h"
 #include "eal_thread.h"
@@ -101,6 +104,8 @@ static struct rte_mem_config early_mem_config;
  * duration of the program, as we hold a write lock on it in the primary proc */
 static int mem_cfg_fd = -1;
 
+static int run_once_reset_internal_config;
+
 static struct flock wr_lock = {
 		.l_type = F_WRLCK,
 		.l_whence = SEEK_SET,
@@ -515,119 +520,135 @@ eal_log_level_parse(int argc, char **argv)
 	optarg = old_optarg;
 }
 
-/* Parse the argument given in the command line of the application */
+/* Parse single argument */
 static int
-eal_parse_args(int argc, char **argv)
+eal_parse_option(int opt, char *optarg, int option_index, char *prgname)
 {
-	int opt, ret;
-	char **argvopt;
-	int option_index;
-	char *prgname = argv[0];
-	const int old_optind = optind;
-	const int old_optopt = optopt;
-	char * const old_optarg = optarg;
+	int ret;
 
-	argvopt = argv;
-	optind = 1;
+	/* getopt is not happy, stop right now */
+	if (opt == '?') {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
 
-	while ((opt = getopt_long(argc, argvopt, eal_short_options,
-				  eal_long_options, &option_index)) != EOF) {
+	ret = eal_parse_common_option(opt, optarg, &internal_config);
+	/* common parser is not happy */
+	if (ret < 0) {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+	/* common parser handled this option */
+	if (ret == 0)
+		return 0;
 
-		/* getopt is not happy, stop right now */
-		if (opt == '?') {
+	switch (opt) {
+	case 'h':
+		eal_usage(prgname);
+		exit(EXIT_SUCCESS);
+		break;
+
+	/* long options */
+	case OPT_XEN_DOM0_NUM:
+#ifdef RTE_LIBRTE_XEN_DOM0
+		internal_config.xen_dom0_support = 1;
+		break;
+#else
+		RTE_LOG(ERR, EAL, "Can't support DPDK app "
+			"running on Dom0, please configure"
+			" RTE_LIBRTE_XEN_DOM0=y\n");
+		ret = -1;
+		goto out;
+#endif
+
+	case OPT_HUGE_DIR_NUM:
+		internal_config.hugepage_dir = optarg;
+		break;
+
+	case OPT_FILE_PREFIX_NUM:
+		internal_config.hugefile_prefix = optarg;
+		break;
+
+	case OPT_SOCKET_MEM_NUM:
+		if (eal_parse_socket_mem(optarg) < 0) {
+			RTE_LOG(ERR, EAL, "invalid parameters for --"
+					OPT_SOCKET_MEM "\n");
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
 		}
+		break;
 
-		ret = eal_parse_common_option(opt, optarg, &internal_config);
-		/* common parser is not happy */
-		if (ret < 0) {
+	case OPT_BASE_VIRTADDR_NUM:
+		if (eal_parse_base_virtaddr(optarg) < 0) {
+			RTE_LOG(ERR, EAL, "invalid parameter for --"
+					OPT_BASE_VIRTADDR "\n");
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
 		}
-		/* common parser handled this option */
-		if (ret == 0)
-			continue;
+		break;
 
-		switch (opt) {
-		case 'h':
+	case OPT_VFIO_INTR_NUM:
+		if (eal_parse_vfio_intr(optarg) < 0) {
+			RTE_LOG(ERR, EAL, "invalid parameters for --"
+					OPT_VFIO_INTR "\n");
 			eal_usage(prgname);
-			exit(EXIT_SUCCESS);
-
-		/* long options */
-		case OPT_XEN_DOM0_NUM:
-#ifdef RTE_LIBRTE_XEN_DOM0
-			internal_config.xen_dom0_support = 1;
-#else
-			RTE_LOG(ERR, EAL, "Can't support DPDK app "
-				"running on Dom0, please configure"
-				" RTE_LIBRTE_XEN_DOM0=y\n");
 			ret = -1;
 			goto out;
-#endif
-			break;
+		}
+		break;
 
-		case OPT_HUGE_DIR_NUM:
-			internal_config.hugepage_dir = optarg;
-			break;
+	case OPT_CREATE_UIO_DEV_NUM:
+		internal_config.create_uio_dev = 1;
+		break;
 
-		case OPT_FILE_PREFIX_NUM:
-			internal_config.hugefile_prefix = optarg;
-			break;
+	default:
+		if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
+			RTE_LOG(ERR, EAL, "Option %c is not supported "
+				"on Linux\n", opt);
+		} else if (opt >= OPT_LONG_MIN_NUM &&
+			   opt < OPT_LONG_MAX_NUM) {
+			RTE_LOG(ERR, EAL, "Option %s is not supported "
+				"on Linux\n",
+				eal_long_options[option_index].name);
+		} else {
+			RTE_LOG(ERR, EAL, "Option %d is not supported "
+				"on Linux\n", opt);
+		}
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
 
-		case OPT_SOCKET_MEM_NUM:
-			if (eal_parse_socket_mem(optarg) < 0) {
-				RTE_LOG(ERR, EAL, "invalid parameters for --"
-						OPT_SOCKET_MEM "\n");
-				eal_usage(prgname);
-				ret = -1;
-				goto out;
-			}
-			break;
+	return 0;
+out:
+	return ret;
+}
 
-		case OPT_BASE_VIRTADDR_NUM:
-			if (eal_parse_base_virtaddr(optarg) < 0) {
-				RTE_LOG(ERR, EAL, "invalid parameter for --"
-						OPT_BASE_VIRTADDR "\n");
-				eal_usage(prgname);
-				ret = -1;
-				goto out;
-			}
-			break;
+/* Parse the argument given in the command line of the application */
+static int
+eal_parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	const int old_optind = optind;
+	const int old_optopt = optopt;
+	char * const old_optarg = optarg;
 
-		case OPT_VFIO_INTR_NUM:
-			if (eal_parse_vfio_intr(optarg) < 0) {
-				RTE_LOG(ERR, EAL, "invalid parameters for --"
-						OPT_VFIO_INTR "\n");
-				eal_usage(prgname);
-				ret = -1;
-				goto out;
-			}
-			break;
+	argvopt = argv;
+	optind = 1;
 
-		case OPT_CREATE_UIO_DEV_NUM:
-			internal_config.create_uio_dev = 1;
-			break;
+	while ((opt = getopt_long(argc, argvopt, eal_short_options,
+				  eal_long_options, &option_index)) != EOF) {
 
-		default:
-			if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
-				RTE_LOG(ERR, EAL, "Option %c is not supported "
-					"on Linux\n", opt);
-			} else if (opt >= OPT_LONG_MIN_NUM &&
-				   opt < OPT_LONG_MAX_NUM) {
-				RTE_LOG(ERR, EAL, "Option %s is not supported "
-					"on Linux\n",
-					eal_long_options[option_index].name);
-			} else {
-				RTE_LOG(ERR, EAL, "Option %d is not supported "
-					"on Linux\n", opt);
-			}
-			eal_usage(prgname);
-			ret = -1;
+		ret = eal_parse_option(opt, optarg, option_index, prgname);
+		if (ret < 0)
 			goto out;
-		}
 	}
 
 	if (eal_adjust_config(&internal_config) != 0) {
@@ -774,7 +795,10 @@ rte_eal_init(int argc, char **argv)
 
 	thread_id = pthread_self();
 
-	eal_reset_internal_config(&internal_config);
+	if (!run_once_reset_internal_config) {
+		eal_reset_internal_config(&internal_config);
+		run_once_reset_internal_config = 1;
+	}
 
 	/* set log level as early as possible */
 	eal_log_level_parse(argc, argv);
@@ -995,3 +1019,68 @@ rte_eal_check_module(const char *module_name)
 	/* Module has been found */
 	return 1;
 }
+
+#ifdef RTE_LIBRTE_CFGFILE
+int
+rte_eal_configure(struct rte_cfgfile *cfg, char *prgname)
+{
+	int n_entries;
+	int i;
+	int opt;
+	int option_index;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, "DPDK");
+
+	if (n_entries < 1) {
+		printf("No DPDK section entries in cfgfile object\n");
+		return 0;
+	}
+
+	struct rte_cfgfile_entry entries[n_entries];
+
+	if (n_entries !=
+			rte_cfgfile_section_entries(cfg, "DPDK", entries,
+					n_entries)) {
+		rte_eal_init_alert("Unexpected fault.");
+		rte_errno = EFAULT;
+		return -1;
+	}
+
+	if (!run_once_reset_internal_config) {
+		eal_reset_internal_config(&internal_config);
+		run_once_reset_internal_config = 1;
+	}
+
+	/* set log level as early as possible */
+	eal_log_level_cfg(cfg);
+
+	if (rte_eal_cpu_init() < 0) {
+		rte_eal_init_alert("Cannot detect lcores.");
+		rte_errno = ENOTSUP;
+		return -1;
+	}
+
+	for (i = 0; i < n_entries; i++) {
+		eal_getopt(entries[i].name, &opt, &option_index);
+
+		if (eal_parse_option(opt, entries[i].value,
+				option_index, prgname) != 0) {
+			rte_eal_init_alert("Invalid config file arguments.");
+			rte_errno = EINVAL;
+			return -1;
+		}
+	}
+
+	if (parse_vdev_devices(cfg) < 0) {
+		rte_eal_init_alert("Couldn't parse vdevs");
+		rte_errno = ENOMEM;
+		return -1;
+	}
+	return 0;
+}
+#endif
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 0f9e009..d206fd3 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -205,6 +205,7 @@ DPDK_17.08 {
 	rte_bus_find;
 	rte_bus_find_by_device;
 	rte_bus_find_by_name;
+	rte_eal_configure;
 
 } DPDK_17.05;
 
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index e239581..6c1b508 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -81,7 +81,6 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_POWER)          += -lrte_power
 
 _LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER)          += -lrte_timer
 _LDLIBS-$(CONFIG_RTE_LIBRTE_EFD)            += -lrte_efd
-_LDLIBS-$(CONFIG_RTE_LIBRTE_CFGFILE)        += -lrte_cfgfile
 
 _LDLIBS-y += --whole-archive
 
@@ -97,6 +96,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL)        += -lrte_mempool
 _LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING)   += -lrte_mempool_ring
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RING)           += -lrte_ring
 _LDLIBS-$(CONFIG_RTE_LIBRTE_EAL)            += -lrte_eal
+_LDLIBS-$(CONFIG_RTE_LIBRTE_CFGFILE)        += -lrte_cfgfile
 _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline
 _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v4 2/3] app/testpmd: add parse options from cfg file
  2017-07-10 12:51           ` [PATCH v4 0/3] EAL change for using a config file for DPDK Kuba Kozak
  2017-07-10 12:51             ` [PATCH v4 1/3] eal: add functions parsing EAL arguments Kuba Kozak
@ 2017-07-10 12:51             ` Kuba Kozak
  2017-07-10 12:51             ` [PATCH v4 3/3] app/testpmd: add parse options from JSON " Kuba Kozak
  2 siblings, 0 replies; 70+ messages in thread
From: Kuba Kozak @ 2017-07-10 12:51 UTC (permalink / raw)
  To: dev
  Cc: deepak.k.jain, bruce.richardson, michalx.k.jastrzebski,
	jacekx.piasecki, Kuba Kozak

This patch shows how to pass arguments to application
using config.ini file.

If a --cfgfile-path <path> option is passed into commandline
non EAL section, then the file is loaded and used by app.

If a config.ini file is present in current working
directory, and no --cfgfile-path option is passed in, config.ini
file will be loaded and used by app as default configuration.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test-pmd/config.ini   |   24 +
 app/test-pmd/parameters.c | 1181 +++++++++++++++++++++++++--------------------
 app/test-pmd/testpmd.c    |   67 ++-
 app/test-pmd/testpmd.h    |    3 +-
 4 files changed, 761 insertions(+), 514 deletions(-)
 create mode 100644 app/test-pmd/config.ini

diff --git a/app/test-pmd/config.ini b/app/test-pmd/config.ini
new file mode 100644
index 0000000..54c83a2
--- /dev/null
+++ b/app/test-pmd/config.ini
@@ -0,0 +1,24 @@
+[DPDK]
+v =
+l = 0-4
+n = 4
+master-lcore = 0
+proc-type = primary
+
+[TEST-PMD]
+i =
+portmask = 0xff
+nb-cores = 4
+port-topology = paired
+
+[DPDK.vdev0]
+net_ring0 =
+
+[DPDK.vdev1]
+net_ring1 =
+
+[DPDK.vdev2]
+net_ring2 =
+
+[DPDK.vdev3]
+net_ring3 =
\ No newline at end of file
diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 958b3d0..d32861a 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -77,9 +77,98 @@
 #include <rte_eth_bond.h>
 #endif
 #include <rte_flow.h>
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#endif
 
 #include "testpmd.h"
 
+enum { TX, RX };
+
+static struct option lgopts[] = {
+	{ "cfgfile-path",		1, 0, 1 },
+	{ "help",			0, 0, 0 },
+#ifdef RTE_LIBRTE_CMDLINE
+	{ "interactive",		0, 0, 0 },
+	{ "cmdline-file",		1, 0, 0 },
+	{ "auto-start",			0, 0, 0 },
+	{ "eth-peers-configfile",	1, 0, 0 },
+	{ "eth-peer",			1, 0, 0 },
+#endif
+	{ "tx-first",			0, 0, 0 },
+	{ "stats-period",		1, 0, 0 },
+	{ "ports",			1, 0, 0 },
+	{ "nb-cores",			1, 0, 0 },
+	{ "nb-ports",			1, 0, 0 },
+	{ "coremask",			1, 0, 0 },
+	{ "portmask",			1, 0, 0 },
+	{ "numa",			0, 0, 0 },
+	{ "no-numa",			0, 0, 0 },
+	{ "mp-anon",			0, 0, 0 },
+	{ "port-numa-config",           1, 0, 0 },
+	{ "ring-numa-config",           1, 0, 0 },
+	{ "socket-num",			1, 0, 0 },
+	{ "mbuf-size",			1, 0, 0 },
+	{ "total-num-mbufs",		1, 0, 0 },
+	{ "max-pkt-len",		1, 0, 0 },
+	{ "pkt-filter-mode",            1, 0, 0 },
+	{ "pkt-filter-report-hash",     1, 0, 0 },
+	{ "pkt-filter-size",            1, 0, 0 },
+	{ "pkt-filter-drop-queue",      1, 0, 0 },
+#ifdef RTE_LIBRTE_LATENCY_STATS
+	{ "latencystats",               1, 0, 0 },
+#endif
+#ifdef RTE_LIBRTE_BITRATE
+	{ "bitrate-stats",              1, 0, 0 },
+#endif
+	{ "disable-crc-strip",          0, 0, 0 },
+	{ "enable-lro",                 0, 0, 0 },
+	{ "enable-rx-cksum",            0, 0, 0 },
+	{ "enable-scatter",             0, 0, 0 },
+	{ "disable-hw-vlan",            0, 0, 0 },
+	{ "disable-hw-vlan-filter",     0, 0, 0 },
+	{ "disable-hw-vlan-strip",      0, 0, 0 },
+	{ "disable-hw-vlan-extend",     0, 0, 0 },
+	{ "enable-drop-en",            0, 0, 0 },
+	{ "disable-rss",                0, 0, 0 },
+	{ "port-topology",              1, 0, 0 },
+	{ "forward-mode",               1, 0, 0 },
+	{ "rss-ip",			0, 0, 0 },
+	{ "rss-udp",			0, 0, 0 },
+	{ "rxq",			1, 0, 0 },
+	{ "txq",			1, 0, 0 },
+	{ "rxd",			1, 0, 0 },
+	{ "txd",			1, 0, 0 },
+	{ "burst",			1, 0, 0 },
+	{ "mbcache",			1, 0, 0 },
+	{ "txpt",			1, 0, 0 },
+	{ "txht",			1, 0, 0 },
+	{ "txwt",			1, 0, 0 },
+	{ "txfreet",			1, 0, 0 },
+	{ "txrst",			1, 0, 0 },
+	{ "txqflags",			1, 0, 0 },
+	{ "rxpt",			1, 0, 0 },
+	{ "rxht",			1, 0, 0 },
+	{ "rxwt",			1, 0, 0 },
+	{ "rxfreet",                    1, 0, 0 },
+	{ "tx-queue-stats-mapping",	1, 0, 0 },
+	{ "rx-queue-stats-mapping",	1, 0, 0 },
+	{ "no-flush-rx",	0, 0, 0 },
+	{ "txpkts",			1, 0, 0 },
+	{ "disable-link-check",		0, 0, 0 },
+	{ "no-lsc-interrupt",		0, 0, 0 },
+	{ "no-rmv-interrupt",		0, 0, 0 },
+	{ "print-event",		1, 0, 0 },
+	{ "mask-event",			1, 0, 0 },
+	{ 0, 0, 0, 0 },
+};
+
+#ifdef RTE_LIBRTE_CMDLINE
+#define SHORTOPTS "i"
+#else
+#define SHORTOPTS ""
+#endif
+
 static void
 usage(char* progname)
 {
@@ -554,578 +643,646 @@ parse_event_printing_config(const char *optarg, int enable)
 	return 0;
 }
 
-void
-launch_args_parse(int argc, char** argv)
+static int
+parse_option(int opt, char *optarg, int opt_idx, char *prgname)
 {
-	int n, opt;
-	char **argvopt;
-	int opt_idx;
-	enum { TX, RX };
+	int n;
 
-	static struct option lgopts[] = {
-		{ "help",			0, 0, 0 },
+	switch (opt) {
 #ifdef RTE_LIBRTE_CMDLINE
-		{ "interactive",		0, 0, 0 },
-		{ "cmdline-file",		1, 0, 0 },
-		{ "auto-start",			0, 0, 0 },
-		{ "eth-peers-configfile",	1, 0, 0 },
-		{ "eth-peer",			1, 0, 0 },
-#endif
-		{ "tx-first",			0, 0, 0 },
-		{ "stats-period",		1, 0, 0 },
-		{ "ports",			1, 0, 0 },
-		{ "nb-cores",			1, 0, 0 },
-		{ "nb-ports",			1, 0, 0 },
-		{ "coremask",			1, 0, 0 },
-		{ "portmask",			1, 0, 0 },
-		{ "numa",			0, 0, 0 },
-		{ "no-numa",			0, 0, 0 },
-		{ "mp-anon",			0, 0, 0 },
-		{ "port-numa-config",           1, 0, 0 },
-		{ "ring-numa-config",           1, 0, 0 },
-		{ "socket-num",			1, 0, 0 },
-		{ "mbuf-size",			1, 0, 0 },
-		{ "total-num-mbufs",		1, 0, 0 },
-		{ "max-pkt-len",		1, 0, 0 },
-		{ "pkt-filter-mode",            1, 0, 0 },
-		{ "pkt-filter-report-hash",     1, 0, 0 },
-		{ "pkt-filter-size",            1, 0, 0 },
-		{ "pkt-filter-drop-queue",      1, 0, 0 },
-#ifdef RTE_LIBRTE_LATENCY_STATS
-		{ "latencystats",               1, 0, 0 },
-#endif
-#ifdef RTE_LIBRTE_BITRATE
-		{ "bitrate-stats",              1, 0, 0 },
+	case 'i':
+		printf("Interactive-mode selected\n");
+		interactive = 1;
+		break;
 #endif
-		{ "disable-crc-strip",          0, 0, 0 },
-		{ "enable-lro",                 0, 0, 0 },
-		{ "enable-rx-cksum",            0, 0, 0 },
-		{ "enable-scatter",             0, 0, 0 },
-		{ "disable-hw-vlan",            0, 0, 0 },
-		{ "disable-hw-vlan-filter",     0, 0, 0 },
-		{ "disable-hw-vlan-strip",      0, 0, 0 },
-		{ "disable-hw-vlan-extend",     0, 0, 0 },
-		{ "enable-drop-en",            0, 0, 0 },
-		{ "disable-rss",                0, 0, 0 },
-		{ "port-topology",              1, 0, 0 },
-		{ "forward-mode",               1, 0, 0 },
-		{ "rss-ip",			0, 0, 0 },
-		{ "rss-udp",			0, 0, 0 },
-		{ "rxq",			1, 0, 0 },
-		{ "txq",			1, 0, 0 },
-		{ "rxd",			1, 0, 0 },
-		{ "txd",			1, 0, 0 },
-		{ "burst",			1, 0, 0 },
-		{ "mbcache",			1, 0, 0 },
-		{ "txpt",			1, 0, 0 },
-		{ "txht",			1, 0, 0 },
-		{ "txwt",			1, 0, 0 },
-		{ "txfreet",			1, 0, 0 },
-		{ "txrst",			1, 0, 0 },
-		{ "txqflags",			1, 0, 0 },
-		{ "rxpt",			1, 0, 0 },
-		{ "rxht",			1, 0, 0 },
-		{ "rxwt",			1, 0, 0 },
-		{ "rxfreet",                    1, 0, 0 },
-		{ "tx-queue-stats-mapping",	1, 0, 0 },
-		{ "rx-queue-stats-mapping",	1, 0, 0 },
-		{ "no-flush-rx",	0, 0, 0 },
-		{ "txpkts",			1, 0, 0 },
-		{ "disable-link-check",		0, 0, 0 },
-		{ "no-lsc-interrupt",		0, 0, 0 },
-		{ "no-rmv-interrupt",		0, 0, 0 },
-		{ "print-event",		1, 0, 0 },
-		{ "mask-event",			1, 0, 0 },
-		{ 0, 0, 0, 0 },
-	};
+	case 'a':
+		printf("Auto-start selected\n");
+		auto_start = 1;
+		break;
 
-	argvopt = argv;
-
-#ifdef RTE_LIBRTE_CMDLINE
-#define SHORTOPTS "i"
-#else
-#define SHORTOPTS ""
-#endif
-	while ((opt = getopt_long(argc, argvopt, SHORTOPTS "ah",
-				 lgopts, &opt_idx)) != EOF) {
-		switch (opt) {
+	case 0: /*long options */
+		if (!strcmp(lgopts[opt_idx].name, "help")) {
+			usage(prgname);
+			rte_exit(EXIT_SUCCESS, "Displayed help\n");
+			return 0;
+		}
 #ifdef RTE_LIBRTE_CMDLINE
-		case 'i':
+		if (!strcmp(lgopts[opt_idx].name, "interactive")) {
 			printf("Interactive-mode selected\n");
 			interactive = 1;
-			break;
-#endif
-		case 'a':
+		}
+		if (!strcmp(lgopts[opt_idx].name, "cmdline-file")) {
+			printf("CLI commands to be read from %s\n",
+			       optarg);
+			snprintf(cmdline_filename,
+				 sizeof(cmdline_filename), "%s",
+				 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "auto-start")) {
 			printf("Auto-start selected\n");
 			auto_start = 1;
-			break;
+		}
+		if (!strcmp(lgopts[opt_idx].name, "tx-first")) {
+			printf("Ports to start sending a burst of "
+					"packets first\n");
+			tx_first = 1;
+		}
+		if (!strcmp(lgopts[opt_idx].name, "stats-period")) {
+			char *end = NULL;
+			unsigned int n;
 
-		case 0: /*long options */
-			if (!strcmp(lgopts[opt_idx].name, "help")) {
-				usage(argv[0]);
-				rte_exit(EXIT_SUCCESS, "Displayed help\n");
-			}
-#ifdef RTE_LIBRTE_CMDLINE
-			if (!strcmp(lgopts[opt_idx].name, "interactive")) {
-				printf("Interactive-mode selected\n");
-				interactive = 1;
-			}
-			if (!strcmp(lgopts[opt_idx].name, "cmdline-file")) {
-				printf("CLI commands to be read from %s\n",
-				       optarg);
-				snprintf(cmdline_filename,
-					 sizeof(cmdline_filename), "%s",
-					 optarg);
-			}
-			if (!strcmp(lgopts[opt_idx].name, "auto-start")) {
-				printf("Auto-start selected\n");
-				auto_start = 1;
-			}
-			if (!strcmp(lgopts[opt_idx].name, "tx-first")) {
-				printf("Ports to start sending a burst of "
-						"packets first\n");
-				tx_first = 1;
-			}
-			if (!strcmp(lgopts[opt_idx].name, "stats-period")) {
-				char *end = NULL;
-				unsigned int n;
+			n = strtoul(optarg, &end, 10);
+			if ((optarg[0] == '\0') || (end == NULL) ||
+					(*end != '\0'))
+				break;
 
-				n = strtoul(optarg, &end, 10);
-				if ((optarg[0] == '\0') || (end == NULL) ||
-						(*end != '\0'))
-					break;
+			stats_period = n;
+			break;
+		}
+		if (!strcmp(lgopts[opt_idx].name,
+			    "eth-peers-configfile")) {
+			if (init_peer_eth_addrs(optarg) != 0)
+				rte_exit(EXIT_FAILURE,
+					 "Cannot open logfile\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "eth-peer")) {
+			char *port_end;
+			uint8_t c, peer_addr[6];
 
-				stats_period = n;
-				break;
-			}
-			if (!strcmp(lgopts[opt_idx].name,
-				    "eth-peers-configfile")) {
-				if (init_peer_eth_addrs(optarg) != 0)
-					rte_exit(EXIT_FAILURE,
-						 "Cannot open logfile\n");
+			errno = 0;
+			n = strtoul(optarg, &port_end, 10);
+			if (errno != 0 || port_end == optarg ||
+					*port_end++ != ',')
+				rte_exit(EXIT_FAILURE,
+					 "Invalid eth-peer: %s", optarg);
+			if (n >= RTE_MAX_ETHPORTS) {
+				rte_exit(EXIT_FAILURE,
+					 "eth-peer: port %d >= "
+					 "RTE_MAX_ETHPORTS(%d)\n",
+					 n, RTE_MAX_ETHPORTS);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "eth-peer")) {
-				char *port_end;
-				uint8_t c, peer_addr[6];
-
-				errno = 0;
-				n = strtoul(optarg, &port_end, 10);
-				if (errno != 0 || port_end == optarg || *port_end++ != ',')
-					rte_exit(EXIT_FAILURE,
-						 "Invalid eth-peer: %s", optarg);
-				if (n >= RTE_MAX_ETHPORTS)
-					rte_exit(EXIT_FAILURE,
-						 "eth-peer: port %d >= RTE_MAX_ETHPORTS(%d)\n",
-						 n, RTE_MAX_ETHPORTS);
 
-				if (cmdline_parse_etheraddr(NULL, port_end,
-						&peer_addr, sizeof(peer_addr)) < 0)
-					rte_exit(EXIT_FAILURE,
-						 "Invalid ethernet address: %s\n",
-						 port_end);
-				for (c = 0; c < 6; c++)
-					peer_eth_addrs[n].addr_bytes[c] =
-						peer_addr[c];
-				nb_peer_eth_addrs++;
+			if (cmdline_parse_etheraddr(NULL, port_end,
+					&peer_addr, sizeof(peer_addr)) < 0) {
+				rte_exit(EXIT_FAILURE,
+					 "Invalid ethernet address: %s\n",
+					 port_end);
+				return -1;
 			}
+			for (c = 0; c < 6; c++)
+				peer_eth_addrs[n].addr_bytes[c] =
+					peer_addr[c];
+			nb_peer_eth_addrs++;
+		}
 #endif
-			if (!strcmp(lgopts[opt_idx].name, "nb-ports")) {
-				n = atoi(optarg);
-				if (n > 0 && n <= nb_ports)
-					nb_fwd_ports = (uint8_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "Invalid port %d\n", n);
+		if (!strcmp(lgopts[opt_idx].name, "nb-ports")) {
+			n = atoi(optarg);
+			if (n > 0 && n <= nb_ports)
+				nb_fwd_ports = (uint8_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "Invalid port %d\n", n);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "nb-cores")) {
-				n = atoi(optarg);
-				if (n > 0 && n <= nb_lcores)
-					nb_fwd_lcores = (uint8_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "nb-cores should be > 0 and <= %d\n",
-						 nb_lcores);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "nb-cores")) {
+			n = atoi(optarg);
+			if (n > 0 && n <= nb_lcores)
+				nb_fwd_lcores = (uint8_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "nb-cores should be > 0 and <= %d\n",
+					 nb_lcores);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "coremask"))
-				parse_fwd_coremask(optarg);
-			if (!strcmp(lgopts[opt_idx].name, "portmask"))
-				parse_fwd_portmask(optarg);
-			if (!strcmp(lgopts[opt_idx].name, "no-numa"))
-				numa_support = 0;
-			if (!strcmp(lgopts[opt_idx].name, "numa"))
-				numa_support = 1;
-			if (!strcmp(lgopts[opt_idx].name, "mp-anon")) {
-				mp_anon = 1;
+		}
+		if (!strcmp(lgopts[opt_idx].name, "coremask"))
+			parse_fwd_coremask(optarg);
+		if (!strcmp(lgopts[opt_idx].name, "portmask"))
+			parse_fwd_portmask(optarg);
+		if (!strcmp(lgopts[opt_idx].name, "no-numa"))
+			numa_support = 0;
+		if (!strcmp(lgopts[opt_idx].name, "numa"))
+			numa_support = 1;
+		if (!strcmp(lgopts[opt_idx].name, "mp-anon"))
+			mp_anon = 1;
+		if (!strcmp(lgopts[opt_idx].name, "port-numa-config")) {
+			if (parse_portnuma_config(optarg)) {
+				rte_exit(EXIT_FAILURE,
+				   "invalid port-numa configuration\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "port-numa-config")) {
-				if (parse_portnuma_config(optarg))
-					rte_exit(EXIT_FAILURE,
-					   "invalid port-numa configuration\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "ring-numa-config"))
+			if (parse_ringnuma_config(optarg)) {
+				rte_exit(EXIT_FAILURE,
+				   "invalid ring-numa configuration\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "ring-numa-config"))
-				if (parse_ringnuma_config(optarg))
-					rte_exit(EXIT_FAILURE,
-					   "invalid ring-numa configuration\n");
-			if (!strcmp(lgopts[opt_idx].name, "socket-num")) {
-				n = atoi(optarg);
-				if (!new_socket_id((uint8_t)n)) {
-					socket_num = (uint8_t)n;
-				} else {
-					print_invalid_socket_id_error();
-					rte_exit(EXIT_FAILURE,
-						"Invalid socket id");
-				}
+		if (!strcmp(lgopts[opt_idx].name, "socket-num")) {
+			n = atoi(optarg);
+			if (!new_socket_id((uint8_t)n)) {
+				socket_num = (uint8_t)n;
+			} else {
+				print_invalid_socket_id_error();
+				rte_exit(EXIT_FAILURE,
+					"Invalid socket id");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "mbuf-size")) {
-				n = atoi(optarg);
-				if (n > 0 && n <= 0xFFFF)
-					mbuf_data_size = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "mbuf-size should be > 0 and < 65536\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "mbuf-size")) {
+			n = atoi(optarg);
+			if (n > 0 && n <= 0xFFFF)
+				mbuf_data_size = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "mbuf-size should be > 0 and < 65536\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "total-num-mbufs")) {
-				n = atoi(optarg);
-				if (n > 1024)
-					param_total_num_mbufs = (unsigned)n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "total-num-mbufs should be > 1024\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "total-num-mbufs")) {
+			n = atoi(optarg);
+			if (n > 1024)
+				param_total_num_mbufs = (unsigned int)n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "total-num-mbufs should be > 1024\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "max-pkt-len")) {
-				n = atoi(optarg);
-				if (n >= ETHER_MIN_LEN) {
-					rx_mode.max_rx_pkt_len = (uint32_t) n;
-					if (n > ETHER_MAX_LEN)
-					    rx_mode.jumbo_frame = 1;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "Invalid max-pkt-len=%d - should be > %d\n",
-						 n, ETHER_MIN_LEN);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "max-pkt-len")) {
+			n = atoi(optarg);
+			if (n >= ETHER_MIN_LEN) {
+				rx_mode.max_rx_pkt_len = (uint32_t) n;
+				if (n > ETHER_MAX_LEN)
+					rx_mode.jumbo_frame = 1;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "Invalid max-pkt-len=%d - should be > %d\n",
+					 n, ETHER_MIN_LEN);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "pkt-filter-mode")) {
-				if (!strcmp(optarg, "signature"))
-					fdir_conf.mode =
-						RTE_FDIR_MODE_SIGNATURE;
-				else if (!strcmp(optarg, "perfect"))
-					fdir_conf.mode = RTE_FDIR_MODE_PERFECT;
-				else if (!strcmp(optarg, "perfect-mac-vlan"))
-					fdir_conf.mode = RTE_FDIR_MODE_PERFECT_MAC_VLAN;
-				else if (!strcmp(optarg, "perfect-tunnel"))
-					fdir_conf.mode = RTE_FDIR_MODE_PERFECT_TUNNEL;
-				else if (!strcmp(optarg, "none"))
-					fdir_conf.mode = RTE_FDIR_MODE_NONE;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "pkt-mode-invalid %s invalid - must be: "
-						 "none, signature, perfect, perfect-mac-vlan"
-						 " or perfect-tunnel\n",
-						 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "pkt-filter-mode")) {
+			if (!strcmp(optarg, "signature"))
+				fdir_conf.mode =
+					RTE_FDIR_MODE_SIGNATURE;
+			else if (!strcmp(optarg, "perfect"))
+				fdir_conf.mode = RTE_FDIR_MODE_PERFECT;
+			else if (!strcmp(optarg, "perfect-mac-vlan"))
+				fdir_conf.mode = RTE_FDIR_MODE_PERFECT_MAC_VLAN;
+			else if (!strcmp(optarg, "perfect-tunnel"))
+				fdir_conf.mode = RTE_FDIR_MODE_PERFECT_TUNNEL;
+			else if (!strcmp(optarg, "none"))
+				fdir_conf.mode = RTE_FDIR_MODE_NONE;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "pkt-mode-invalid %s invalid - must be: "
+					 "none, signature, perfect, perfect-mac-vlan"
+					 " or perfect-tunnel\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name,
-				    "pkt-filter-report-hash")) {
-				if (!strcmp(optarg, "none"))
-					fdir_conf.status =
-						RTE_FDIR_NO_REPORT_STATUS;
-				else if (!strcmp(optarg, "match"))
-					fdir_conf.status =
-						RTE_FDIR_REPORT_STATUS;
-				else if (!strcmp(optarg, "always"))
-					fdir_conf.status =
-						RTE_FDIR_REPORT_STATUS_ALWAYS;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "pkt-filter-report-hash %s invalid "
-						 "- must be: none or match or always\n",
-						 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name,
+			    "pkt-filter-report-hash")) {
+			if (!strcmp(optarg, "none"))
+				fdir_conf.status =
+					RTE_FDIR_NO_REPORT_STATUS;
+			else if (!strcmp(optarg, "match"))
+				fdir_conf.status =
+					RTE_FDIR_REPORT_STATUS;
+			else if (!strcmp(optarg, "always"))
+				fdir_conf.status =
+					RTE_FDIR_REPORT_STATUS_ALWAYS;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "pkt-filter-report-hash %s invalid "
+					 "- must be: none or match or always\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "pkt-filter-size")) {
-				if (!strcmp(optarg, "64K"))
-					fdir_conf.pballoc =
-						RTE_FDIR_PBALLOC_64K;
-				else if (!strcmp(optarg, "128K"))
-					fdir_conf.pballoc =
-						RTE_FDIR_PBALLOC_128K;
-				else if (!strcmp(optarg, "256K"))
-					fdir_conf.pballoc =
-						RTE_FDIR_PBALLOC_256K;
-				else
-					rte_exit(EXIT_FAILURE, "pkt-filter-size %s invalid -"
-						 " must be: 64K or 128K or 256K\n",
-						 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "pkt-filter-size")) {
+			if (!strcmp(optarg, "64K"))
+				fdir_conf.pballoc =
+					RTE_FDIR_PBALLOC_64K;
+			else if (!strcmp(optarg, "128K"))
+				fdir_conf.pballoc =
+					RTE_FDIR_PBALLOC_128K;
+			else if (!strcmp(optarg, "256K"))
+				fdir_conf.pballoc =
+					RTE_FDIR_PBALLOC_256K;
+			else {
+				rte_exit(EXIT_FAILURE, "pkt-filter-size %s invalid -"
+					 " must be: 64K or 128K or 256K\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name,
-				    "pkt-filter-drop-queue")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					fdir_conf.drop_queue = (uint8_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "drop queue %d invalid - must"
-						 "be >= 0 \n", n);
+		}
+		if (!strcmp(lgopts[opt_idx].name,
+			    "pkt-filter-drop-queue")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				fdir_conf.drop_queue = (uint8_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "drop queue %d invalid - must "
+					 "be >= 0\n", n);
+				return -1;
 			}
+		}
 #ifdef RTE_LIBRTE_LATENCY_STATS
-			if (!strcmp(lgopts[opt_idx].name,
-				    "latencystats")) {
-				n = atoi(optarg);
-				if (n >= 0) {
-					latencystats_lcore_id = (lcoreid_t) n;
-					latencystats_enabled = 1;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "invalid lcore id %d for latencystats"
-						 " must be >= 0\n", n);
+		if (!strcmp(lgopts[opt_idx].name,
+			    "latencystats")) {
+			n = atoi(optarg);
+			if (n >= 0) {
+				latencystats_lcore_id = (lcoreid_t) n;
+				latencystats_enabled = 1;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "invalid lcore id %d for latencystats"
+					 " must be >= 0\n", n);
+				return -1;
 			}
+		}
 #endif
 #ifdef RTE_LIBRTE_BITRATE
-			if (!strcmp(lgopts[opt_idx].name, "bitrate-stats")) {
-				n = atoi(optarg);
-				if (n >= 0) {
-					bitrate_lcore_id = (lcoreid_t) n;
-					bitrate_enabled = 1;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "invalid lcore id %d for bitrate stats"
-						 " must be >= 0\n", n);
+		if (!strcmp(lgopts[opt_idx].name, "bitrate-stats")) {
+			n = atoi(optarg);
+			if (n >= 0) {
+				bitrate_lcore_id = (lcoreid_t) n;
+				bitrate_enabled = 1;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "invalid lcore id %d for bitrate stats"
+					 " must be >= 0\n", n);
+				return -1;
 			}
+		}
 #endif
-			if (!strcmp(lgopts[opt_idx].name, "disable-crc-strip"))
-				rx_mode.hw_strip_crc = 0;
-			if (!strcmp(lgopts[opt_idx].name, "enable-lro"))
-				rx_mode.enable_lro = 1;
-			if (!strcmp(lgopts[opt_idx].name, "enable-scatter"))
-				rx_mode.enable_scatter = 1;
-			if (!strcmp(lgopts[opt_idx].name, "enable-rx-cksum"))
-				rx_mode.hw_ip_checksum = 1;
-
-			if (!strcmp(lgopts[opt_idx].name, "disable-hw-vlan")) {
-				rx_mode.hw_vlan_filter = 0;
-				rx_mode.hw_vlan_strip  = 0;
-				rx_mode.hw_vlan_extend = 0;
-			}
+		if (!strcmp(lgopts[opt_idx].name, "disable-crc-strip"))
+			rx_mode.hw_strip_crc = 0;
+		if (!strcmp(lgopts[opt_idx].name, "enable-lro"))
+			rx_mode.enable_lro = 1;
+		if (!strcmp(lgopts[opt_idx].name, "enable-scatter"))
+			rx_mode.enable_scatter = 1;
+		if (!strcmp(lgopts[opt_idx].name, "enable-rx-cksum"))
+			rx_mode.hw_ip_checksum = 1;
 
-			if (!strcmp(lgopts[opt_idx].name,
-					"disable-hw-vlan-filter"))
-				rx_mode.hw_vlan_filter = 0;
-
-			if (!strcmp(lgopts[opt_idx].name,
-					"disable-hw-vlan-strip"))
-				rx_mode.hw_vlan_strip  = 0;
-
-			if (!strcmp(lgopts[opt_idx].name,
-					"disable-hw-vlan-extend"))
-				rx_mode.hw_vlan_extend = 0;
-
-			if (!strcmp(lgopts[opt_idx].name, "enable-drop-en"))
-				rx_drop_en = 1;
-
-			if (!strcmp(lgopts[opt_idx].name, "disable-rss"))
-				rss_hf = 0;
-			if (!strcmp(lgopts[opt_idx].name, "port-topology")) {
-				if (!strcmp(optarg, "paired"))
-					port_topology = PORT_TOPOLOGY_PAIRED;
-				else if (!strcmp(optarg, "chained"))
-					port_topology = PORT_TOPOLOGY_CHAINED;
-				else if (!strcmp(optarg, "loop"))
-					port_topology = PORT_TOPOLOGY_LOOP;
-				else
-					rte_exit(EXIT_FAILURE, "port-topology %s invalid -"
-						 " must be: paired or chained \n",
-						 optarg);
+		if (!strcmp(lgopts[opt_idx].name, "disable-hw-vlan")) {
+			rx_mode.hw_vlan_filter = 0;
+			rx_mode.hw_vlan_strip  = 0;
+			rx_mode.hw_vlan_extend = 0;
+		}
+
+		if (!strcmp(lgopts[opt_idx].name,
+				"disable-hw-vlan-filter"))
+			rx_mode.hw_vlan_filter = 0;
+
+		if (!strcmp(lgopts[opt_idx].name,
+				"disable-hw-vlan-strip"))
+			rx_mode.hw_vlan_strip  = 0;
+
+		if (!strcmp(lgopts[opt_idx].name,
+				"disable-hw-vlan-extend"))
+			rx_mode.hw_vlan_extend = 0;
+
+		if (!strcmp(lgopts[opt_idx].name, "enable-drop-en"))
+			rx_drop_en = 1;
+
+		if (!strcmp(lgopts[opt_idx].name, "disable-rss"))
+			rss_hf = 0;
+		if (!strcmp(lgopts[opt_idx].name, "port-topology")) {
+			if (!strcmp(optarg, "paired"))
+				port_topology = PORT_TOPOLOGY_PAIRED;
+			else if (!strcmp(optarg, "chained"))
+				port_topology = PORT_TOPOLOGY_CHAINED;
+			else if (!strcmp(optarg, "loop"))
+				port_topology = PORT_TOPOLOGY_LOOP;
+			else {
+				rte_exit(EXIT_FAILURE, "port-topology %s invalid -"
+					 " must be: paired or chained\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "forward-mode"))
-				set_pkt_forwarding_mode(optarg);
-			if (!strcmp(lgopts[opt_idx].name, "rss-ip"))
-				rss_hf = ETH_RSS_IP;
-			if (!strcmp(lgopts[opt_idx].name, "rss-udp"))
-				rss_hf = ETH_RSS_UDP;
-			if (!strcmp(lgopts[opt_idx].name, "rxq")) {
-				n = atoi(optarg);
-				if (n >= 0 && n <= (int) MAX_QUEUE_ID)
-					nb_rxq = (queueid_t) n;
-				else
-					rte_exit(EXIT_FAILURE, "rxq %d invalid - must be"
-						  " >= 0 && <= %d\n", n,
-						  (int) MAX_QUEUE_ID);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "forward-mode"))
+			set_pkt_forwarding_mode(optarg);
+		if (!strcmp(lgopts[opt_idx].name, "rss-ip"))
+			rss_hf = ETH_RSS_IP;
+		if (!strcmp(lgopts[opt_idx].name, "rss-udp"))
+			rss_hf = ETH_RSS_UDP;
+		if (!strcmp(lgopts[opt_idx].name, "rxq")) {
+			n = atoi(optarg);
+			if (n >= 0 && n <= (int) MAX_QUEUE_ID)
+				nb_rxq = (queueid_t) n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxq %d invalid - must be"
+					  " >= 0 && <= %d\n", n,
+					  (int) MAX_QUEUE_ID);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txq")) {
-				n = atoi(optarg);
-				if (n >= 0 && n <= (int) MAX_QUEUE_ID)
-					nb_txq = (queueid_t) n;
-				else
-					rte_exit(EXIT_FAILURE, "txq %d invalid - must be"
-						  " >= 0 && <= %d\n", n,
-						  (int) MAX_QUEUE_ID);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txq")) {
+			n = atoi(optarg);
+			if (n >= 0 && n <= (int) MAX_QUEUE_ID)
+				nb_txq = (queueid_t) n;
+			else {
+				rte_exit(EXIT_FAILURE, "txq %d invalid - must be"
+					  " >= 0 && <= %d\n", n,
+					  (int) MAX_QUEUE_ID);
+				return -1;
 			}
-			if (!nb_rxq && !nb_txq) {
-				rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
-						"be non-zero\n");
+		}
+		if (!nb_rxq && !nb_txq) {
+			rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
+					"be non-zero\n");
+			return -1;
+		}
+		if (!strcmp(lgopts[opt_idx].name, "burst")) {
+			n = atoi(optarg);
+			if ((n >= 1) && (n <= MAX_PKT_BURST))
+				nb_pkt_per_burst = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "burst must >= 1 and <= %d]",
+					 MAX_PKT_BURST);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "burst")) {
-				n = atoi(optarg);
-				if ((n >= 1) && (n <= MAX_PKT_BURST))
-					nb_pkt_per_burst = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "burst must >= 1 and <= %d]",
-						 MAX_PKT_BURST);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "mbcache")) {
+			n = atoi(optarg);
+			if ((n >= 0) &&
+			    (n <= RTE_MEMPOOL_CACHE_MAX_SIZE))
+				mb_mempool_cache = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "mbcache must be >= 0 and <= %d\n",
+					 RTE_MEMPOOL_CACHE_MAX_SIZE);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "mbcache")) {
-				n = atoi(optarg);
-				if ((n >= 0) &&
-				    (n <= RTE_MEMPOOL_CACHE_MAX_SIZE))
-					mb_mempool_cache = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "mbcache must be >= 0 and <= %d\n",
-						 RTE_MEMPOOL_CACHE_MAX_SIZE);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txfreet")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_free_thresh = (int16_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txfreet must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txfreet")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_free_thresh = (int16_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txfreet must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txrst")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_rs_thresh = (int16_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txrst must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txrst")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_rs_thresh = (int16_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txrst must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txqflags")) {
+			char *end = NULL;
+
+			n = strtoul(optarg, &end, 16);
+			if (n >= 0)
+				txq_flags = (int32_t)n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "txqflags must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txqflags")) {
-				char *end = NULL;
-				n = strtoul(optarg, &end, 16);
-				if (n >= 0)
-					txq_flags = (int32_t)n;
-				else
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxd")) {
+			n = atoi(optarg);
+			if (n > 0) {
+				if (rx_free_thresh >= n)
 					rte_exit(EXIT_FAILURE,
-						 "txqflags must be >= 0\n");
+						 "rxd must be > "
+						 "rx_free_thresh(%d)\n",
+						 (int)rx_free_thresh);
+				else
+					nb_rxd = (uint16_t) n;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "rxd(%d) invalid - must be > 0\n",
+					 n);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxd")) {
-				n = atoi(optarg);
-				if (n > 0) {
-					if (rx_free_thresh >= n)
-						rte_exit(EXIT_FAILURE,
-							 "rxd must be > "
-							 "rx_free_thresh(%d)\n",
-							 (int)rx_free_thresh);
-					else
-						nb_rxd = (uint16_t) n;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "rxd(%d) invalid - must be > 0\n",
-						 n);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txd")) {
+			n = atoi(optarg);
+			if (n > 0)
+				nb_txd = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE, "txd must be in > 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txd")) {
-				n = atoi(optarg);
-				if (n > 0)
-					nb_txd = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE, "txd must be in > 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txpt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_pthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txpt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txpt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_pthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txpt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txht")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_hthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txht must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txht")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_hthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txht must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txwt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_wthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txwt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txwt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_wthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txwt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxpt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_pthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxpt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxpt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_pthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxpt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxht")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_hthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxht must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxht")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_hthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxht must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxwt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_wthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxwt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxwt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_wthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxwt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxfreet")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_free_thresh = (int16_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxfreet must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxfreet")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_free_thresh = (int16_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxfreet must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "tx-queue-stats-mapping")) {
+			if (parse_queue_stats_mapping_config(optarg, TX)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid TX queue statistics mapping config entered\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "tx-queue-stats-mapping")) {
-				if (parse_queue_stats_mapping_config(optarg, TX)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid TX queue statistics mapping config entered\n");
-				}
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rx-queue-stats-mapping")) {
+			if (parse_queue_stats_mapping_config(optarg, RX)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid RX queue statistics mapping config entered\n");
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rx-queue-stats-mapping")) {
-				if (parse_queue_stats_mapping_config(optarg, RX)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid RX queue statistics mapping config entered\n");
-				}
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txpkts")) {
+			unsigned int seg_lengths[RTE_MAX_SEGS_PER_PKT];
+			unsigned int nb_segs;
+
+			nb_segs = parse_item_list(optarg, "txpkt segments",
+					RTE_MAX_SEGS_PER_PKT, seg_lengths, 0);
+			if (nb_segs > 0)
+				set_tx_pkt_segments(seg_lengths, nb_segs);
+			else {
+				rte_exit(EXIT_FAILURE, "bad txpkts\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txpkts")) {
-				unsigned seg_lengths[RTE_MAX_SEGS_PER_PKT];
-				unsigned int nb_segs;
-
-				nb_segs = parse_item_list(optarg, "txpkt segments",
-						RTE_MAX_SEGS_PER_PKT, seg_lengths, 0);
-				if (nb_segs > 0)
-					set_tx_pkt_segments(seg_lengths, nb_segs);
-				else
-					rte_exit(EXIT_FAILURE, "bad txpkts\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "no-flush-rx"))
+			no_flush_rx = 1;
+		if (!strcmp(lgopts[opt_idx].name, "disable-link-check"))
+			no_link_check = 1;
+		if (!strcmp(lgopts[opt_idx].name, "no-lsc-interrupt"))
+			lsc_interrupt = 0;
+		if (!strcmp(lgopts[opt_idx].name, "no-rmv-interrupt"))
+			rmv_interrupt = 0;
+		if (!strcmp(lgopts[opt_idx].name, "print-event"))
+			if (parse_event_printing_config(optarg, 1)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid print-event argument\n");
+				return -1;
+			}
+		if (!strcmp(lgopts[opt_idx].name, "mask-event"))
+			if (parse_event_printing_config(optarg, 0)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid mask-event argument\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "no-flush-rx"))
-				no_flush_rx = 1;
-			if (!strcmp(lgopts[opt_idx].name, "disable-link-check"))
-				no_link_check = 1;
-			if (!strcmp(lgopts[opt_idx].name, "no-lsc-interrupt"))
-				lsc_interrupt = 0;
-			if (!strcmp(lgopts[opt_idx].name, "no-rmv-interrupt"))
-				rmv_interrupt = 0;
-			if (!strcmp(lgopts[opt_idx].name, "print-event"))
-				if (parse_event_printing_config(optarg, 1)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid print-event argument\n");
-				}
-			if (!strcmp(lgopts[opt_idx].name, "mask-event"))
-				if (parse_event_printing_config(optarg, 0)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid mask-event argument\n");
-				}
 
-			break;
-		case 'h':
-			usage(argv[0]);
-			rte_exit(EXIT_SUCCESS, "Displayed help\n");
-			break;
-		default:
-			usage(argv[0]);
-			rte_exit(EXIT_FAILURE,
-				 "Command line is incomplete or incorrect\n");
+		break;
+	case 'h':
+		usage(prgname);
+		rte_exit(EXIT_SUCCESS, "Displayed help\n");
+		return -1;
+	case 1:
+		/* does nothing*/
+		break;
+	default:
+		usage(prgname);
+		rte_exit(EXIT_FAILURE,
+			 "Command line is incomplete or incorrect\n");
+		return -1;
+	}
+	return 0;
+}
+
+void
+launch_args_parse(int argc, char **argv)
+{
+	int opt;
+	char **argvopt;
+	int opt_idx;
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, SHORTOPTS "ah",
+				 lgopts, &opt_idx)) != EOF) {
+		parse_option(opt, optarg, opt_idx, argv[0]);
+	}
+}
+
+#ifdef RTE_LIBRTE_CFGFILE
+static void
+non_eal_getopt(const char *str, int *opt, int *option_index)
+{
+	int i;
+
+	*opt = '?';
+	*option_index = 0;
+
+	if (strlen(str) == 1) {
+		*opt = *str;
+		return;
+	}
+
+	for (i = 0; lgopts[i].name != NULL; i++) {
+		if (strcmp(str, lgopts[i].name) == 0) {
+			*opt = lgopts[i].val;
+			*option_index = i;
 			break;
 		}
 	}
 }
+#endif
+
+#ifdef RTE_LIBRTE_CFGFILE
+#define APP_SECTION "TEST-PMD"
+int non_eal_configure(struct rte_cfgfile *cfg, char *prgname)
+{
+	int n_entries;
+	int i;
+	int opt;
+	int option_index;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, APP_SECTION);
+
+	if (n_entries < 1) {
+		printf("No %s section entries "
+				"in cfgfile object\n", APP_SECTION);
+		return -1;
+	}
+
+	struct rte_cfgfile_entry entries[n_entries];
+
+	if (n_entries !=
+		rte_cfgfile_section_entries(cfg, APP_SECTION, entries,
+				n_entries)) {
+		rte_exit(EXIT_FAILURE, "Unexpected fault");
+		return -1;
+	}
+
+	for (i = 0; i < n_entries; i++) {
+		non_eal_getopt(entries[i].name, &opt, &option_index);
+
+		parse_option(opt, entries[i].value,
+				option_index, prgname);
+	}
+	return 0;
+}
+#endif
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index e09b803..7b82976 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -91,6 +91,9 @@
 #include <rte_latencystats.h>
 #endif
 #include <rte_gro.h>
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#endif
 
 #include "testpmd.h"
 
@@ -2266,15 +2269,66 @@ signal_handler(int signum)
 	}
 }
 
+#ifdef RTE_LIBRTE_CFGFILE
+/* Load config file path from command line */
+static char *
+cfgfile_load_path(int argc, char **argv)
+{
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		if (!strcmp("--cfgfile-path", argv[i])) {
+			if (argv[i+1] && argv[i+1][0] != '-')
+				return strdup(argv[i+1]);
+		} else if (!strncmp("--cfgfile-path=", argv[i], 15)) {
+			char *ptr = argv[i];
+
+			ptr += 15;
+			if (strlen(ptr))
+				return strdup(ptr);
+		}
+	}
+	return NULL;
+}
+#endif
+
+#define APP_NAME "TEST-PMD"
 int
 main(int argc, char** argv)
 {
 	int  diag;
 	uint8_t port_id;
+#ifdef RTE_LIBRTE_CFGFILE
+	struct rte_cfgfile *cfg = NULL;
+	char *config_file = NULL;
+#endif
 
 	signal(SIGINT, signal_handler);
 	signal(SIGTERM, signal_handler);
 
+#ifdef RTE_LIBRTE_CFGFILE
+	/* load --cfgfile-path argument from argv */
+	config_file = cfgfile_load_path(argc, argv);
+
+	if (config_file) {
+		printf("Info: found cfgfile-path \"%s\"\n", config_file);
+	} else {
+		printf("Info: not found cfgfile-path parameter "
+				"(searching for cfgfile "
+				"in default directory)\n");
+		config_file = strdup("config.ini");
+	}
+
+	cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+
+	if (cfg == NULL) {
+		printf("Info: Valid cfgfile not found\n");
+	} else {
+		diag = rte_eal_configure(cfg, argv[0]);
+		if (diag < 0)
+			rte_panic("Cannot init EAL\n");
+	}
+#endif
 	diag = rte_eal_init(argc, argv);
 	if (diag < 0)
 		rte_panic("Cannot init EAL\n");
@@ -2304,6 +2358,18 @@ main(int argc, char** argv)
 	latencystats_enabled = 0;
 #endif
 
+#ifdef RTE_LIBRTE_CFGFILE
+	if (cfg != NULL) {
+		non_eal_configure(cfg, argv[0]);
+		rte_cfgfile_close(cfg);
+		cfg = 0;
+
+		if (config_file) {
+			free(config_file);
+			config_file = 0;
+		}
+	}
+#endif
 	argc -= diag;
 	argv += diag;
 	if (argc > 1)
@@ -2400,6 +2466,5 @@ main(int argc, char** argv)
 		if (rc < 0)
 			return 1;
 	}
-
 	return 0;
 }
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 73985c3..8ff4d88 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -512,7 +512,7 @@ port_pci_reg_write(struct rte_port *port, uint32_t reg_off, uint32_t reg_v)
 unsigned int parse_item_list(char* str, const char* item_name,
 			unsigned int max_items,
 			unsigned int *parsed_items, int check_unique_values);
-void launch_args_parse(int argc, char** argv);
+void launch_args_parse(int argc, char **argv);
 void cmdline_read_from_file(const char *filename);
 void prompt(void);
 void prompt_exit(void);
@@ -656,6 +656,7 @@ enum print_warning {
 };
 int port_id_is_invalid(portid_t port_id, enum print_warning warning);
 int new_socket_id(unsigned int socket_id);
+int non_eal_configure(struct rte_cfgfile *cfg, char *prgname);
 
 /*
  * Work-around of a compilation error with ICC on invocations of the
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v4 3/3] app/testpmd: add parse options from JSON cfg file
  2017-07-10 12:51           ` [PATCH v4 0/3] EAL change for using a config file for DPDK Kuba Kozak
  2017-07-10 12:51             ` [PATCH v4 1/3] eal: add functions parsing EAL arguments Kuba Kozak
  2017-07-10 12:51             ` [PATCH v4 2/3] app/testpmd: add parse options from cfg file Kuba Kozak
@ 2017-07-10 12:51             ` Kuba Kozak
  2 siblings, 0 replies; 70+ messages in thread
From: Kuba Kozak @ 2017-07-10 12:51 UTC (permalink / raw)
  To: dev
  Cc: deepak.k.jain, bruce.richardson, michalx.k.jastrzebski,
	jacekx.piasecki, Kuba Kozak

This patch shows usage of Jansson library to parse
application arguments from JSON config file.

https://github.com/akheron/jansson

If a --cfgfile-path <path> option is passed into commandline
non EAL section, then the disired JSON file is loaded and used
by app. In case when JSON doesn't exist an INI file is loaded.
The INI file can be passed also from --cfgfile-path option.

If a file called config.ini is present in current working
directory, and no --cfgfile-path option is passed in, config.ini
file will be loaded and used by app.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test-pmd/Makefile    |  6 ++++
 app/test-pmd/config.json | 33 +++++++++++++++++
 app/test-pmd/testpmd.c   | 94 +++++++++++++++++++++++++++++++++++++++++++++++-
 config/common_base       |  5 +++
 4 files changed, 137 insertions(+), 1 deletion(-)
 create mode 100644 app/test-pmd/config.json

diff --git a/app/test-pmd/Makefile b/app/test-pmd/Makefile
index c36be19..a1c84cc 100644
--- a/app/test-pmd/Makefile
+++ b/app/test-pmd/Makefile
@@ -83,6 +83,12 @@ endif
 
 endif
 
+ifeq ($(CONFIG_RTE_JSON_SUPPORT),y)
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -ljansson
+endif
+endif
+
 CFLAGS_cmdline.o := -D_GNU_SOURCE
 
 include $(RTE_SDK)/mk/rte.app.mk
diff --git a/app/test-pmd/config.json b/app/test-pmd/config.json
new file mode 100644
index 0000000..4589dbc
--- /dev/null
+++ b/app/test-pmd/config.json
@@ -0,0 +1,33 @@
+{
+	"DPDK":
+	{
+		"v": "",
+		"l": "0-4",
+		"n": "4",
+		"master-lcore": "0",
+		"proc-type": "primary"
+	},
+	"TEST-PMD":
+	{
+		"i": "",
+		"portmask": "0xff",
+		"nb-cores": "4",
+		"port-topology": "paired"
+	},
+	"DPDK.vdev0":
+	{
+		"net_ring0": ""
+	},
+	"DPDK.vdev1":
+	{
+		"net_ring1": ""
+	},
+	"DPDK.vdev2":
+	{
+		"net_ring2": ""
+	},
+	"DPDK.vdev3":
+	{
+		"net_ring3": ""
+	}
+}
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 7b82976..014bb46 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -46,6 +46,10 @@
 
 #include <stdint.h>
 #include <unistd.h>
+
+#ifdef RTE_JSON_SUPPORT
+#include <jansson.h>
+#endif
 #include <inttypes.h>
 
 #include <rte_common.h>
@@ -2290,6 +2294,87 @@ cfgfile_load_path(int argc, char **argv)
 	}
 	return NULL;
 }
+
+#ifdef RTE_JSON_SUPPORT
+/*
+ * Decoding JSON structure to rte_cfgfile structure.
+ * Returns handler to cfgfile object, NULL if error.
+ */
+static struct
+rte_cfgfile *l3fwd_json_to_cfg(json_t *json, int flags)
+{
+	if (!json) {
+		printf("Error: JSON structure is NULL, nothing to parse\n");
+		return NULL;
+	}
+	/* create an empty instance of cfgfile structure */
+	struct rte_cfgfile *cfgfile = rte_cfgfile_create(flags);
+
+	if (!cfgfile)
+		return NULL;
+
+	const char *section;
+	json_t *entry;
+
+	/* set pointer to first section */
+	void *iter_section = json_object_iter(json);
+
+	while (iter_section) {
+
+		section = json_object_iter_key(iter_section);
+		entry = json_object_iter_value(iter_section);
+
+		/* add parsed section name of current section to cfgfile */
+		rte_cfgfile_add_section(cfgfile, section);
+
+		/* set pointer to first entry */
+		void *iter_entry = json_object_iter(entry);
+
+		while (iter_entry) {
+
+			const char *key;
+			const char *value;
+
+			key = json_object_iter_key(iter_entry);
+			value = json_string_value(
+					json_object_iter_value(iter_entry));
+
+			/* add parsed key and value of current entry */
+			/* to cfgfile */
+			rte_cfgfile_add_entry(cfgfile, section, key, value);
+
+			/* pointer to next entry */
+			iter_entry = json_object_iter_next(entry, iter_entry);
+		}
+		/* pointer to next section */
+		iter_section = json_object_iter_next(json, iter_section);
+	}
+	return cfgfile;
+}
+
+/*
+ * Check presence and load JSON file to rte_cfgfile structure.
+ * Returns handler to cfgfile object, NULL if error.
+ */
+static struct
+rte_cfgfile *l3fwd_load_json_to_cfg(const char *path)
+{
+	struct rte_cfgfile *cfgfile = NULL;
+	/* check if config file exist */
+	if (access(path, F_OK) != -1) {
+		/* parse JSON file */
+		json_error_t error;
+		json_t *json = json_load_file(path, 0, &error);
+
+		if (json)
+			cfgfile = l3fwd_json_to_cfg(json, 0);
+		else
+			fprintf(stderr, "JSON error on line %d: %s\n",
+							error.line, error.text);
+	}
+	return cfgfile;
+}
+#endif
 #endif
 
 #define APP_NAME "TEST-PMD"
@@ -2318,9 +2403,16 @@ main(int argc, char** argv)
 				"in default directory)\n");
 		config_file = strdup("config.ini");
 	}
-
+#ifndef RTE_JSON_SUPPORT
 	cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+#endif
 
+#ifdef RTE_JSON_SUPPORT
+	if (strstr(config_file, ".ini"))
+		cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+	else if (strstr(config_file, ".json"))
+		cfg = l3fwd_load_json_to_cfg(config_file);
+#endif
 	if (cfg == NULL) {
 		printf("Info: Valid cfgfile not found\n");
 	} else {
diff --git a/config/common_base b/config/common_base
index 8ae6e92..98e8fa2 100644
--- a/config/common_base
+++ b/config/common_base
@@ -744,3 +744,8 @@ CONFIG_RTE_APP_CRYPTO_PERF=y
 # Compile the eventdev application
 #
 CONFIG_RTE_APP_EVENTDEV=y
+
+#
+# Compile JSON support
+#
+CONFIG_RTE_JSON_SUPPORT=n
\ No newline at end of file
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [PATCH v4 0/5] Rework cfgfile API to enable apps config file support
  2017-07-10 12:44           ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Jacek Piasecki
                               ` (4 preceding siblings ...)
  2017-07-10 12:44             ` [PATCH v4 5/5] test/cfgfile: add new unit test Jacek Piasecki
@ 2017-07-10 15:13             ` Thomas Monjalon
  2017-07-20 21:48               ` Thomas Monjalon
  5 siblings, 1 reply; 70+ messages in thread
From: Thomas Monjalon @ 2017-07-10 15:13 UTC (permalink / raw)
  To: Jacek Piasecki, Cristian Dumitrescu
  Cc: dev, bruce.richardson, deepak.k.jain, kubax.kozak, michalx.k.jastrzebski

The maintainer of librte_cfgfile is Cristian (+Cc),
please set him as recipient for next time.

10/07/2017 14:44, Jacek Piasecki:
> New API for cfgfile library allows to create a cfgfile at runtime, add new
> section, add entry in a section, update existing entry and save cfgfile
> structure to INI file - opens up the possibility to have applications
> dynamically build up a proper DPDK configuration, rather than
> having to have a pre-existing one. Due the new API functions, simplification
> of load() function was made. One new unit test to TEST app was added. It
> contains an example of a large INI file whose parsing requires multiple
> reallocation of memory.
> 
> ---
> v4:	
> 	Change members of structure cfgfile:
> 	- struct *sections[] to *sections
> 	- struct *entries[] to *entries
> 	- remove free_sections and free_entries
> 	Rework of existing cfgfile API functions to work with modified
> 	rte_cfgfile struct.
> 	Rework of malloc/realloc implementation due rte_cfgfile struct change,
> 	reducing mulitiple mallocs.
> 	Change return error codes for all library functions (errno.h)
> 	Checkpatch fixes
> v3: 
> 	split one patchset into two distinct patchsets:
> 	1. cfgfile library and TEST app changes
> 	2. EAL changes and examples (this patchset depends on cfgfile)
> v2:
>   lib eal:
>   	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
> 	Now this function load data from cfg structure and did initial
> 	initialization of EAL arguments. Vdev argument are stored in different
> 	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
> 	function it is necessary to call rte_eal_init to complete EAL
> 	initialization. There is no more merging arguments from different
> 	sources (cfg file and command line).
>   	Added non_eal_configure to testpmd application.
> 	Function maintain the same functionality as rte_eal_configure but
> 	for non-eal arguments. 
>   	Added config JSON feature to testpmd last patch from patchset contain
> 	example showing use of .json configuration files.
> 
>   lib cfgfile:
>   	Rework of add_section(), add_entry() new implementation
>   	New members allocated_entries/sections, free_entries/sections
> 	in rte_cfgfile structure, change in array of pointers
> 	**sections, **entries instead of *sections[], *entries[]
>   	Add  set_entry() to update/overwrite already existing entry in cfgfile
> 	struct
>   	Add save() function to save on disc cfgfile structure in INI format
>   	Rework of existing load() function  simplifying the code
>   	Add unit test realloc_sections() in TEST app for testing realloc/malloc
> 	of new API functions, add test for save() function
> 
> Jacek Piasecki (5):
>   cfgfile: remove EAL dependency
>   cfgfile: change existing API functions
>   cfgfile: add new functions to API
>   cfgfile: rework of load function
>   test/cfgfile: add new unit test
> 
>  lib/Makefile                                     |   3 +-
>  lib/librte_cfgfile/Makefile                      |   1 +
>  lib/librte_cfgfile/rte_cfgfile.c                 | 417 ++++++++++++++---------
>  lib/librte_cfgfile/rte_cfgfile.h                 |  82 ++++-
>  lib/librte_cfgfile/rte_cfgfile_version.map       |  11 +
>  test/test/test_cfgfile.c                         |  40 +++
>  test/test/test_cfgfiles/etc/realloc_sections.ini | 128 +++++++
>  7 files changed, 514 insertions(+), 168 deletions(-)
>  create mode 100644 test/test/test_cfgfiles/etc/realloc_sections.ini
> 
> 

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v5 0/3] EAL change for using a config file for DPDK
  2017-07-10 12:51             ` [PATCH v4 1/3] eal: add functions parsing EAL arguments Kuba Kozak
@ 2017-07-13  9:26               ` Kuba Kozak
  2017-07-13 10:07               ` Kuba Kozak
  1 sibling, 0 replies; 70+ messages in thread
From: Kuba Kozak @ 2017-07-13  9:26 UTC (permalink / raw)
  To: dev
  Cc: deepak.k.jain, bruce.richardson, michalx.k.jastrzebski,
	jacekx.piasecki, Kuba Kozak

This patchset introduce a mechanism for running dpdk application with 
parameters provided by configuration file.

A new API for EAL takes a config file data type - either loaded from
file, or built up programmatically in the application - and extracts
DPDK parameters from it to be used when eal init is called. 
This allows apps to have an alternative method to configure EAL,
other than via command-line parameters.

Reworked applications are used to demonstrate the new eal API.
If a --cfgfile-path <path> option is passed into command line non
EAL section, then the file is loaded and used by app. If a file
called config.ini is present in current working directory, and
no --cfgfile-path option is passed in, config.ini file will be
loaded and used by app.

Patch "app/testpmd: add parse options from JSON cfg file" 
demonstrates the usage of JSON instead of INI file format. 
JSON file can be called the same way as above, 
through --cfgfile-path <path> argument.
---
this patch depends on:
"Rework cfgfile API to enable apps config file support"

v5:
  changed define "RTE_DEVTYPE_VIRTUAL" to "RTE_DEVTYPE_UNDEFINED"
  due to compilation errors (changes on current master).

v4:
 Code optimalisation in parse_vdev_devices() function.
 Moved some functions from librte_eal/bsdapp and librte_eal/linuxapp
 to the librte_eal/common.
 Bug fixes.

v3: 
 split one patchset into two distinct patchsets:
 1. cfgfile library and TEST app changes
 2. EAL changes and examples (this patchset depends on cfgfile)

v2:
  lib eal:
	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
	Now this function load data from cfg structure and did initial
	initialization of EAL arguments. Vdev argument are stored in different
	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
	function it is necessary to call rte_eal_init to complete EAL
	initialization. There is no more merging arguments from different
	sources (cfg file and command line).
  	Added non_eal_configure to testpmd application.
	Function maintain the same functionality as rte_eal_configure but
	for non-eal arguments. 
  	Added config JSON feature to testpmd last patch from patchset contain
	example showing use of .json configuration files.

  lib cfgfile:
  	Rework of add_section(), add_entry() new implementation
  	New members allocated_entries/sections, free_entries/sections
	in rte_cfgfile structure, change in array of pointers
	**sections, **entries instead of *sections[], *entries[]
  	Add  set_entry() to update/overwrite already existing entry in cfgfile
	struct
  	Add save() function to save on disc cfgfile structure in INI format
  	Rework of existing load() function  simplifying the code
  	Add unit test realloc_sections() in TEST app for testing realloc/malloc
	of new API functions, add test for save() function

Kuba Kozak (3):
  eal: add functions parsing EAL arguments
  app/testpmd: add parse options from cfg file
  app/testpmd: add parse options from JSON cfg file

 app/test-pmd/Makefile                           |    6 +
 app/test-pmd/config.ini                         |   24 +
 app/test-pmd/config.json                        |   33 +
 app/test-pmd/parameters.c                       | 1181 +++++++++++++----------
 app/test-pmd/testpmd.c                          |  159 ++-
 app/test-pmd/testpmd.h                          |    3 +-
 config/common_base                              |    5 +
 lib/Makefile                                    |    3 +
 lib/librte_eal/bsdapp/eal/Makefile              |    4 +
 lib/librte_eal/bsdapp/eal/eal.c                 |  165 +++-
 lib/librte_eal/bsdapp/eal/rte_eal_version.map   |    1 +
 lib/librte_eal/common/eal_common_lcore.c        |    7 +
 lib/librte_eal/common/eal_common_options.c      |  106 ++
 lib/librte_eal/common/eal_options.h             |    3 +
 lib/librte_eal/common/include/rte_eal.h         |   20 +
 lib/librte_eal/linuxapp/eal/Makefile            |    3 +
 lib/librte_eal/linuxapp/eal/eal.c               |  265 +++--
 lib/librte_eal/linuxapp/eal/rte_eal_version.map |    1 +
 mk/rte.app.mk                                   |    2 +-
 19 files changed, 1349 insertions(+), 642 deletions(-)
 create mode 100644 app/test-pmd/config.ini
 create mode 100644 app/test-pmd/config.json

-- 
2.7.4

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v5 0/3] EAL change for using a config file for DPDK
  2017-07-10 12:51             ` [PATCH v4 1/3] eal: add functions parsing EAL arguments Kuba Kozak
  2017-07-13  9:26               ` [PATCH v5 0/3] EAL change for using a config file for DPDK Kuba Kozak
@ 2017-07-13 10:07               ` Kuba Kozak
  2017-07-13 10:07                 ` [PATCH v5 1/3] eal: add functions parsing EAL arguments Kuba Kozak
                                   ` (3 more replies)
  1 sibling, 4 replies; 70+ messages in thread
From: Kuba Kozak @ 2017-07-13 10:07 UTC (permalink / raw)
  To: dev
  Cc: deepak.k.jain, bruce.richardson, michalx.k.jastrzebski,
	jacekx.piasecki, Kuba Kozak

This patchset introduce a mechanism for running dpdk application with 
parameters provided by configuration file.

A new API for EAL takes a config file data type - either loaded from
file, or built up programmatically in the application - and extracts
DPDK parameters from it to be used when eal init is called. 
This allows apps to have an alternative method to configure EAL,
other than via command-line parameters.

Reworked applications are used to demonstrate the new eal API.
If a --cfgfile-path <path> option is passed into command line non
EAL section, then the file is loaded and used by app. If a file
called config.ini is present in current working directory, and
no --cfgfile-path option is passed in, config.ini file will be
loaded and used by app.

Patch "app/testpmd: add parse options from JSON cfg file" 
demonstrates the usage of JSON instead of INI file format. 
JSON file can be called the same way as above, 
through --cfgfile-path <path> argument.
---
this patch depends on:
"Rework cfgfile API to enable apps config file support"

v5:
  changed define "RTE_DEVTYPE_VIRTUAL" to "RTE_DEVTYPE_UNDEFINED"
  due to compilation errors (changes on current master).

v4:
 Code optimalisation in parse_vdev_devices() function.
 Moved some functions from librte_eal/bsdapp and librte_eal/linuxapp
 to the librte_eal/common.
 Bug fixes.

v3: 
 split one patchset into two distinct patchsets:
 1. cfgfile library and TEST app changes
 2. EAL changes and examples (this patchset depends on cfgfile)

v2:
  lib eal:
	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
	Now this function load data from cfg structure and did initial
	initialization of EAL arguments. Vdev argument are stored in different
	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
	function it is necessary to call rte_eal_init to complete EAL
	initialization. There is no more merging arguments from different
	sources (cfg file and command line).
  	Added non_eal_configure to testpmd application.
	Function maintain the same functionality as rte_eal_configure but
	for non-eal arguments. 
  	Added config JSON feature to testpmd last patch from patchset contain
	example showing use of .json configuration files.

  lib cfgfile:
  	Rework of add_section(), add_entry() new implementation
  	New members allocated_entries/sections, free_entries/sections
	in rte_cfgfile structure, change in array of pointers
	**sections, **entries instead of *sections[], *entries[]
  	Add  set_entry() to update/overwrite already existing entry in cfgfile
	struct
  	Add save() function to save on disc cfgfile structure in INI format
  	Rework of existing load() function  simplifying the code
  	Add unit test realloc_sections() in TEST app for testing realloc/malloc
	of new API functions, add test for save() function

Kuba Kozak (3):
  eal: add functions parsing EAL arguments
  app/testpmd: add parse options from cfg file
  app/testpmd: add parse options from JSON cfg file

 app/test-pmd/Makefile                           |    6 +
 app/test-pmd/config.ini                         |   24 +
 app/test-pmd/config.json                        |   33 +
 app/test-pmd/parameters.c                       | 1181 +++++++++++++----------
 app/test-pmd/testpmd.c                          |  159 ++-
 app/test-pmd/testpmd.h                          |    3 +-
 config/common_base                              |    5 +
 lib/Makefile                                    |    3 +
 lib/librte_eal/bsdapp/eal/Makefile              |    4 +
 lib/librte_eal/bsdapp/eal/eal.c                 |  165 +++-
 lib/librte_eal/bsdapp/eal/rte_eal_version.map   |    1 +
 lib/librte_eal/common/eal_common_lcore.c        |    7 +
 lib/librte_eal/common/eal_common_options.c      |  106 ++
 lib/librte_eal/common/eal_options.h             |    3 +
 lib/librte_eal/common/include/rte_eal.h         |   20 +
 lib/librte_eal/linuxapp/eal/Makefile            |    3 +
 lib/librte_eal/linuxapp/eal/eal.c               |  265 +++--
 lib/librte_eal/linuxapp/eal/rte_eal_version.map |    1 +
 mk/rte.app.mk                                   |    2 +-
 19 files changed, 1349 insertions(+), 642 deletions(-)
 create mode 100644 app/test-pmd/config.ini
 create mode 100644 app/test-pmd/config.json

-- 
2.7.4

^ permalink raw reply	[flat|nested] 70+ messages in thread

* [PATCH v5 1/3] eal: add functions parsing EAL arguments
  2017-07-13 10:07               ` Kuba Kozak
@ 2017-07-13 10:07                 ` Kuba Kozak
  2017-07-13 10:07                 ` [PATCH v5 2/3] app/testpmd: add parse options from cfg file Kuba Kozak
                                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 70+ messages in thread
From: Kuba Kozak @ 2017-07-13 10:07 UTC (permalink / raw)
  To: dev
  Cc: deepak.k.jain, bruce.richardson, michalx.k.jastrzebski,
	jacekx.piasecki, Kuba Kozak

added function rte_eal_configure which configure
Environment Abstraction Layer (EAL) using
configuration structure.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 lib/Makefile                                    |   3 +
 lib/librte_eal/bsdapp/eal/Makefile              |   4 +
 lib/librte_eal/bsdapp/eal/eal.c                 | 165 +++++++++++----
 lib/librte_eal/bsdapp/eal/rte_eal_version.map   |   1 +
 lib/librte_eal/common/eal_common_lcore.c        |   7 +
 lib/librte_eal/common/eal_common_options.c      | 106 ++++++++++
 lib/librte_eal/common/eal_options.h             |   3 +
 lib/librte_eal/common/include/rte_eal.h         |  20 ++
 lib/librte_eal/linuxapp/eal/Makefile            |   3 +
 lib/librte_eal/linuxapp/eal/eal.c               | 265 ++++++++++++++++--------
 lib/librte_eal/linuxapp/eal/rte_eal_version.map |   1 +
 mk/rte.app.mk                                   |   2 +-
 12 files changed, 452 insertions(+), 128 deletions(-)

diff --git a/lib/Makefile b/lib/Makefile
index 1080a95..2c6c380 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -34,6 +34,9 @@ include $(RTE_SDK)/mk/rte.vars.mk
 DIRS-y += librte_compat
 DIRS-$(CONFIG_RTE_LIBRTE_CFGFILE) += librte_cfgfile
 DIRS-$(CONFIG_RTE_LIBRTE_EAL) += librte_eal
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+DEPDIRS-librte_eal := librte_cfgfile
+endif
 DIRS-$(CONFIG_RTE_LIBRTE_RING) += librte_ring
 DEPDIRS-librte_ring := librte_eal
 DIRS-$(CONFIG_RTE_LIBRTE_MEMPOOL) += librte_mempool
diff --git a/lib/librte_eal/bsdapp/eal/Makefile b/lib/librte_eal/bsdapp/eal/Makefile
index a0f9950..d70eefb 100644
--- a/lib/librte_eal/bsdapp/eal/Makefile
+++ b/lib/librte_eal/bsdapp/eal/Makefile
@@ -50,6 +50,10 @@ EXPORT_MAP := rte_eal_version.map
 
 LIBABIVER := 4
 
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -lrte_cfgfile
+endif
+
 # specific to bsdapp exec-env
 SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) := eal.c
 SRCS-$(CONFIG_RTE_EXEC_ENV_BSDAPP) += eal_memory.c
diff --git a/lib/librte_eal/bsdapp/eal/eal.c b/lib/librte_eal/bsdapp/eal/eal.c
index 05f0c1f..4a0c221 100644
--- a/lib/librte_eal/bsdapp/eal/eal.c
+++ b/lib/librte_eal/bsdapp/eal/eal.c
@@ -73,6 +73,7 @@
 #include <rte_version.h>
 #include <rte_atomic.h>
 #include <malloc_heap.h>
+#include <rte_cfgfile.h>
 
 #include "eal_private.h"
 #include "eal_thread.h"
@@ -99,6 +100,8 @@ static struct flock wr_lock = {
 		.l_len = sizeof(early_mem_config.memseg),
 };
 
+static int run_once_reset_internal_config;
+
 /* Address of global and public configuration */
 static struct rte_config rte_config = {
 		.mem_config = &early_mem_config,
@@ -347,6 +350,58 @@ eal_log_level_parse(int argc, char **argv)
 	optarg = old_optarg;
 }
 
+/* Parse single argument */
+static int
+eal_parse_option(int opt, char *optarg, int option_index, char *prgname)
+{
+	int ret;
+
+	/* getopt is not happy, stop right now */
+	if (opt == '?') {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+
+	ret = eal_parse_common_option(opt, optarg, &internal_config);
+	/* common parser is not happy */
+	if (ret < 0) {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+	/* common parser handled this option */
+	if (ret == 0)
+		return 0;
+
+	switch (opt) {
+	case 'h':
+		eal_usage(prgname);
+		exit(EXIT_SUCCESS);
+		break;
+
+	default:
+		if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
+			RTE_LOG(ERR, EAL, "Option %c is not supported "
+				"on FreeBSD\n", opt);
+		} else if (opt >= OPT_LONG_MIN_NUM &&
+			   opt < OPT_LONG_MAX_NUM) {
+			RTE_LOG(ERR, EAL, "Option %s is not supported "
+				"on FreeBSD\n",
+				eal_long_options[option_index].name);
+		} else {
+			RTE_LOG(ERR, EAL, "Option %d is not supported "
+				"on FreeBSD\n", opt);
+		}
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+	return 0;
+out:
+	return ret;
+}
+
 /* Parse the argument given in the command line of the application */
 static int
 eal_parse_args(int argc, char **argv)
@@ -367,45 +422,9 @@ eal_parse_args(int argc, char **argv)
 	while ((opt = getopt_long(argc, argvopt, eal_short_options,
 				  eal_long_options, &option_index)) != EOF) {
 
-		/* getopt is not happy, stop right now */
-		if (opt == '?') {
-			eal_usage(prgname);
-			ret = -1;
-			goto out;
-		}
-
-		ret = eal_parse_common_option(opt, optarg, &internal_config);
-		/* common parser is not happy */
-		if (ret < 0) {
-			eal_usage(prgname);
-			ret = -1;
-			goto out;
-		}
-		/* common parser handled this option */
-		if (ret == 0)
-			continue;
-
-		switch (opt) {
-		case 'h':
-			eal_usage(prgname);
-			exit(EXIT_SUCCESS);
-		default:
-			if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
-				RTE_LOG(ERR, EAL, "Option %c is not supported "
-					"on FreeBSD\n", opt);
-			} else if (opt >= OPT_LONG_MIN_NUM &&
-				   opt < OPT_LONG_MAX_NUM) {
-				RTE_LOG(ERR, EAL, "Option %s is not supported "
-					"on FreeBSD\n",
-					eal_long_options[option_index].name);
-			} else {
-				RTE_LOG(ERR, EAL, "Option %d is not supported "
-					"on FreeBSD\n", opt);
-			}
-			eal_usage(prgname);
-			ret = -1;
+		ret = eal_parse_option(opt, optarg, option_index, prgname);
+		if (ret < 0)
 			goto out;
-		}
 	}
 
 	if (eal_adjust_config(&internal_config) != 0) {
@@ -517,7 +536,10 @@ rte_eal_init(int argc, char **argv)
 
 	thread_id = pthread_self();
 
-	eal_reset_internal_config(&internal_config);
+	if (!run_once_reset_internal_config) {
+		eal_reset_internal_config(&internal_config);
+		run_once_reset_internal_config = 1;
+	}
 
 	/* set log level as early as possible */
 	eal_log_level_parse(argc, argv);
@@ -677,3 +699,68 @@ rte_eal_process_type(void)
 {
 	return rte_config.process_type;
 }
+
+#ifdef RTE_LIBRTE_CFGFILE
+int
+rte_eal_configure(struct rte_cfgfile *cfg, char *prgname)
+{
+	int n_entries;
+	int i;
+	int opt;
+	int option_index;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, "DPDK");
+
+	if (n_entries < 1) {
+		printf("No DPDK section entries in cfgfile object\n");
+		return 0;
+	}
+
+	struct rte_cfgfile_entry entries[n_entries];
+
+	if (n_entries !=
+			rte_cfgfile_section_entries(cfg, "DPDK", entries,
+					n_entries)) {
+		rte_eal_init_alert("Unexpected fault.");
+		rte_errno = EFAULT;
+		return -1;
+	}
+
+	if (!run_once_reset_internal_config) {
+		eal_reset_internal_config(&internal_config);
+		run_once_reset_internal_config = 1;
+	}
+
+	/* set log level as early as possible */
+	eal_log_level_cfg(cfg);
+
+	if (rte_eal_cpu_init() < 0) {
+		rte_eal_init_alert("Cannot detect lcores.");
+		rte_errno = ENOTSUP;
+		return -1;
+	}
+
+	for (i = 0; i < n_entries; i++) {
+		eal_getopt(entries[i].name, &opt, &option_index);
+
+		if (eal_parse_option(opt, entries[i].value,
+				option_index, prgname) != 0) {
+			rte_eal_init_alert("Invalid config file arguments.");
+			rte_errno = EINVAL;
+			return -1;
+		}
+	}
+
+	if (parse_vdev_devices(cfg) < 0) {
+		rte_eal_init_alert("Couldn't parse vdevs");
+		rte_errno = ENOMEM;
+		return -1;
+	}
+	return 0;
+}
+#endif
diff --git a/lib/librte_eal/bsdapp/eal/rte_eal_version.map b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
index 381f895..932f990 100644
--- a/lib/librte_eal/bsdapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/bsdapp/eal/rte_eal_version.map
@@ -200,6 +200,7 @@ DPDK_17.08 {
 	rte_bus_find;
 	rte_bus_find_by_device;
 	rte_bus_find_by_name;
+	rte_eal_configure;
 
 } DPDK_17.05;
 
diff --git a/lib/librte_eal/common/eal_common_lcore.c b/lib/librte_eal/common/eal_common_lcore.c
index 84fa0cb..d0f4c6f 100644
--- a/lib/librte_eal/common/eal_common_lcore.c
+++ b/lib/librte_eal/common/eal_common_lcore.c
@@ -53,11 +53,18 @@
 int
 rte_eal_cpu_init(void)
 {
+	static int run_once;
+	static int ret;
 	/* pointer to global configuration */
 	struct rte_config *config = rte_eal_get_configuration();
 	unsigned lcore_id;
 	unsigned count = 0;
 
+	/* No need to calculate this function again if we know the result */
+	if (run_once)
+		return ret;
+	run_once = 1;
+
 	/*
 	 * Parse the maximum set of logical cores, detect the subset of running
 	 * ones and enable them by default.
diff --git a/lib/librte_eal/common/eal_common_options.c b/lib/librte_eal/common/eal_common_options.c
index 075b0ea..aaf0f2c 100644
--- a/lib/librte_eal/common/eal_common_options.c
+++ b/lib/librte_eal/common/eal_common_options.c
@@ -50,6 +50,10 @@
 #include <rte_version.h>
 #include <rte_devargs.h>
 #include <rte_memcpy.h>
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#include <rte_errno.h>
+#endif
 
 #include "eal_internal_cfg.h"
 #include "eal_options.h"
@@ -1078,3 +1082,105 @@ eal_common_usage(void)
 	       "  --"OPT_NO_SHCONF"         No shared config (mmap'd files)\n"
 	       "\n", RTE_MAX_LCORE);
 }
+
+#ifdef RTE_LIBRTE_CFGFILE
+/* Parse the arguments for --log-level only */
+void
+eal_log_level_cfg(struct rte_cfgfile *cfg)
+{
+	const char *entry;
+
+	entry = rte_cfgfile_get_entry(cfg, "DPDK", OPT_LOG_LEVEL);
+	if (entry)
+		eal_parse_common_option(OPT_LOG_LEVEL_NUM, entry,
+				&internal_config);
+}
+#endif
+
+#ifdef RTE_LIBRTE_CFGFILE
+static void rte_eal_init_alert(const char *msg)
+{
+	fprintf(stderr, "EAL: FATAL: %s\n", msg);
+	RTE_LOG(ERR, EAL, "%s\n", msg);
+}
+
+#define vdev_buff_size		200
+#define sectionname_size	20
+int
+parse_vdev_devices(struct rte_cfgfile *cfg)
+{
+	char sectionname[sectionname_size] = "DPDK.vdev0";
+	char buffer[vdev_buff_size];
+	int vdev_nb = 0;
+	int n_entries;
+	int buf_len;
+	char *buf_ptr;
+	int i;
+	int ret;
+
+	/* ----------- parsing VDEVS */
+	for (vdev_nb = 1; rte_cfgfile_has_section(cfg, sectionname);
+			vdev_nb++) {
+		n_entries = rte_cfgfile_section_num_entries(cfg, sectionname);
+
+		struct rte_cfgfile_entry entries[n_entries];
+
+		if (n_entries != rte_cfgfile_section_entries(cfg, sectionname,
+				entries, n_entries)) {
+			rte_eal_init_alert("Unexpected fault.");
+			rte_errno = EFAULT;
+			return -1;
+		}
+
+		buffer[0] = '\0';
+		buf_ptr = buffer;
+		buf_len = vdev_buff_size;
+		for (i = 0; i < n_entries; i++) {
+			ret = snprintf(buf_ptr, buf_len, "%s%s%s%s",
+					entries[i].name,
+					(entries[i].value[0] != '\0') ?
+							"=" : "",
+					entries[i].value,
+					(i < (n_entries - 1)) ? "," : "");
+			if (ret >= buf_len) {
+				printf("parse_vdev_devices(): buffer[] size is "
+						"to small\n");
+				return -1;
+			}
+			buf_len -= ret;
+			buf_ptr += ret;
+		}
+
+		/* parsing vdev */
+		if (rte_eal_devargs_add(RTE_DEVTYPE_UNDEFINED,
+				buffer) < 0) {
+			return -1;
+		}
+		snprintf(sectionname, sectionname_size, "DPDK.vdev%d", vdev_nb);
+	}
+	/* ----------- parsing VDEVS */
+	return 0;
+}
+
+void
+eal_getopt(const char *str, int *opt, int *option_index)
+{
+	int i;
+
+	*opt = '?';
+	*option_index = 0;
+
+	if (strlen(str) == 1) {
+		*opt = *str;
+		return;
+	}
+
+	for (i = 0; eal_long_options[i].name != NULL; i++) {
+		if (strcmp(str, eal_long_options[i].name) == 0) {
+			*opt = eal_long_options[i].val;
+			*option_index = i;
+			break;
+		}
+	}
+}
+#endif
diff --git a/lib/librte_eal/common/eal_options.h b/lib/librte_eal/common/eal_options.h
index a881c62..0fce11c 100644
--- a/lib/librte_eal/common/eal_options.h
+++ b/lib/librte_eal/common/eal_options.h
@@ -96,5 +96,8 @@ int eal_check_common_options(struct internal_config *internal_cfg);
 void eal_common_usage(void);
 enum rte_proc_type_t eal_proc_type_detect(void);
 int eal_plugins_init(void);
+void eal_log_level_cfg(struct rte_cfgfile *cfg);
+int parse_vdev_devices(struct rte_cfgfile *cfg);
+void eal_getopt(const char *str, int *opt, int *option_index);
 
 #endif /* EAL_OPTIONS_H */
diff --git a/lib/librte_eal/common/include/rte_eal.h b/lib/librte_eal/common/include/rte_eal.h
index 6b7c5ca..a1c78c0 100644
--- a/lib/librte_eal/common/include/rte_eal.h
+++ b/lib/librte_eal/common/include/rte_eal.h
@@ -46,6 +46,8 @@
 #include <rte_per_lcore.h>
 #include <rte_config.h>
 
+struct rte_cfgfile; /* forward declaration of struct */
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -188,6 +190,24 @@ int rte_eal_iopl_init(void);
  */
 int rte_eal_init(int argc, char **argv);
 
+#ifdef RTE_LIBRTE_CFGFILE
+/**
+ * Initialize the Environment Abstraction Layer (EAL) using
+ * configuration structure
+ *
+ * @param cfg
+ *   pointer to config file structure.
+ * @param prgname
+ *   pointer to string with execution path
+ *
+ * @return
+ *  - On success, return 0
+ *  - On failure, returns -1.
+ */
+int
+rte_eal_configure(struct rte_cfgfile *cfg, char *prgname);
+#endif
+
 /**
  * Check if a primary process is currently alive
  *
diff --git a/lib/librte_eal/linuxapp/eal/Makefile b/lib/librte_eal/linuxapp/eal/Makefile
index 8651e27..7c1c559 100644
--- a/lib/librte_eal/linuxapp/eal/Makefile
+++ b/lib/librte_eal/linuxapp/eal/Makefile
@@ -53,6 +53,9 @@ LDLIBS += -lrt
 ifeq ($(CONFIG_RTE_EAL_NUMA_AWARE_HUGEPAGES),y)
 LDLIBS += -lnuma
 endif
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -lrte_cfgfile
+endif
 
 # specific to linuxapp exec-env
 SRCS-$(CONFIG_RTE_EXEC_ENV_LINUXAPP) := eal.c
diff --git a/lib/librte_eal/linuxapp/eal/eal.c b/lib/librte_eal/linuxapp/eal/eal.c
index 7c78f2d..f09aeff 100644
--- a/lib/librte_eal/linuxapp/eal/eal.c
+++ b/lib/librte_eal/linuxapp/eal/eal.c
@@ -78,6 +78,9 @@
 #include <rte_version.h>
 #include <rte_atomic.h>
 #include <malloc_heap.h>
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#endif
 
 #include "eal_private.h"
 #include "eal_thread.h"
@@ -101,6 +104,8 @@ static struct rte_mem_config early_mem_config;
  * duration of the program, as we hold a write lock on it in the primary proc */
 static int mem_cfg_fd = -1;
 
+static int run_once_reset_internal_config;
+
 static struct flock wr_lock = {
 		.l_type = F_WRLCK,
 		.l_whence = SEEK_SET,
@@ -515,119 +520,135 @@ eal_log_level_parse(int argc, char **argv)
 	optarg = old_optarg;
 }
 
-/* Parse the argument given in the command line of the application */
+/* Parse single argument */
 static int
-eal_parse_args(int argc, char **argv)
+eal_parse_option(int opt, char *optarg, int option_index, char *prgname)
 {
-	int opt, ret;
-	char **argvopt;
-	int option_index;
-	char *prgname = argv[0];
-	const int old_optind = optind;
-	const int old_optopt = optopt;
-	char * const old_optarg = optarg;
+	int ret;
 
-	argvopt = argv;
-	optind = 1;
+	/* getopt is not happy, stop right now */
+	if (opt == '?') {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
 
-	while ((opt = getopt_long(argc, argvopt, eal_short_options,
-				  eal_long_options, &option_index)) != EOF) {
+	ret = eal_parse_common_option(opt, optarg, &internal_config);
+	/* common parser is not happy */
+	if (ret < 0) {
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
+	/* common parser handled this option */
+	if (ret == 0)
+		return 0;
 
-		/* getopt is not happy, stop right now */
-		if (opt == '?') {
+	switch (opt) {
+	case 'h':
+		eal_usage(prgname);
+		exit(EXIT_SUCCESS);
+		break;
+
+	/* long options */
+	case OPT_XEN_DOM0_NUM:
+#ifdef RTE_LIBRTE_XEN_DOM0
+		internal_config.xen_dom0_support = 1;
+		break;
+#else
+		RTE_LOG(ERR, EAL, "Can't support DPDK app "
+			"running on Dom0, please configure"
+			" RTE_LIBRTE_XEN_DOM0=y\n");
+		ret = -1;
+		goto out;
+#endif
+
+	case OPT_HUGE_DIR_NUM:
+		internal_config.hugepage_dir = optarg;
+		break;
+
+	case OPT_FILE_PREFIX_NUM:
+		internal_config.hugefile_prefix = optarg;
+		break;
+
+	case OPT_SOCKET_MEM_NUM:
+		if (eal_parse_socket_mem(optarg) < 0) {
+			RTE_LOG(ERR, EAL, "invalid parameters for --"
+					OPT_SOCKET_MEM "\n");
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
 		}
+		break;
 
-		ret = eal_parse_common_option(opt, optarg, &internal_config);
-		/* common parser is not happy */
-		if (ret < 0) {
+	case OPT_BASE_VIRTADDR_NUM:
+		if (eal_parse_base_virtaddr(optarg) < 0) {
+			RTE_LOG(ERR, EAL, "invalid parameter for --"
+					OPT_BASE_VIRTADDR "\n");
 			eal_usage(prgname);
 			ret = -1;
 			goto out;
 		}
-		/* common parser handled this option */
-		if (ret == 0)
-			continue;
+		break;
 
-		switch (opt) {
-		case 'h':
+	case OPT_VFIO_INTR_NUM:
+		if (eal_parse_vfio_intr(optarg) < 0) {
+			RTE_LOG(ERR, EAL, "invalid parameters for --"
+					OPT_VFIO_INTR "\n");
 			eal_usage(prgname);
-			exit(EXIT_SUCCESS);
-
-		/* long options */
-		case OPT_XEN_DOM0_NUM:
-#ifdef RTE_LIBRTE_XEN_DOM0
-			internal_config.xen_dom0_support = 1;
-#else
-			RTE_LOG(ERR, EAL, "Can't support DPDK app "
-				"running on Dom0, please configure"
-				" RTE_LIBRTE_XEN_DOM0=y\n");
 			ret = -1;
 			goto out;
-#endif
-			break;
+		}
+		break;
 
-		case OPT_HUGE_DIR_NUM:
-			internal_config.hugepage_dir = optarg;
-			break;
+	case OPT_CREATE_UIO_DEV_NUM:
+		internal_config.create_uio_dev = 1;
+		break;
 
-		case OPT_FILE_PREFIX_NUM:
-			internal_config.hugefile_prefix = optarg;
-			break;
+	default:
+		if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
+			RTE_LOG(ERR, EAL, "Option %c is not supported "
+				"on Linux\n", opt);
+		} else if (opt >= OPT_LONG_MIN_NUM &&
+			   opt < OPT_LONG_MAX_NUM) {
+			RTE_LOG(ERR, EAL, "Option %s is not supported "
+				"on Linux\n",
+				eal_long_options[option_index].name);
+		} else {
+			RTE_LOG(ERR, EAL, "Option %d is not supported "
+				"on Linux\n", opt);
+		}
+		eal_usage(prgname);
+		ret = -1;
+		goto out;
+	}
 
-		case OPT_SOCKET_MEM_NUM:
-			if (eal_parse_socket_mem(optarg) < 0) {
-				RTE_LOG(ERR, EAL, "invalid parameters for --"
-						OPT_SOCKET_MEM "\n");
-				eal_usage(prgname);
-				ret = -1;
-				goto out;
-			}
-			break;
+	return 0;
+out:
+	return ret;
+}
 
-		case OPT_BASE_VIRTADDR_NUM:
-			if (eal_parse_base_virtaddr(optarg) < 0) {
-				RTE_LOG(ERR, EAL, "invalid parameter for --"
-						OPT_BASE_VIRTADDR "\n");
-				eal_usage(prgname);
-				ret = -1;
-				goto out;
-			}
-			break;
+/* Parse the argument given in the command line of the application */
+static int
+eal_parse_args(int argc, char **argv)
+{
+	int opt, ret;
+	char **argvopt;
+	int option_index;
+	char *prgname = argv[0];
+	const int old_optind = optind;
+	const int old_optopt = optopt;
+	char * const old_optarg = optarg;
 
-		case OPT_VFIO_INTR_NUM:
-			if (eal_parse_vfio_intr(optarg) < 0) {
-				RTE_LOG(ERR, EAL, "invalid parameters for --"
-						OPT_VFIO_INTR "\n");
-				eal_usage(prgname);
-				ret = -1;
-				goto out;
-			}
-			break;
+	argvopt = argv;
+	optind = 1;
 
-		case OPT_CREATE_UIO_DEV_NUM:
-			internal_config.create_uio_dev = 1;
-			break;
+	while ((opt = getopt_long(argc, argvopt, eal_short_options,
+				  eal_long_options, &option_index)) != EOF) {
 
-		default:
-			if (opt < OPT_LONG_MIN_NUM && isprint(opt)) {
-				RTE_LOG(ERR, EAL, "Option %c is not supported "
-					"on Linux\n", opt);
-			} else if (opt >= OPT_LONG_MIN_NUM &&
-				   opt < OPT_LONG_MAX_NUM) {
-				RTE_LOG(ERR, EAL, "Option %s is not supported "
-					"on Linux\n",
-					eal_long_options[option_index].name);
-			} else {
-				RTE_LOG(ERR, EAL, "Option %d is not supported "
-					"on Linux\n", opt);
-			}
-			eal_usage(prgname);
-			ret = -1;
+		ret = eal_parse_option(opt, optarg, option_index, prgname);
+		if (ret < 0)
 			goto out;
-		}
 	}
 
 	if (eal_adjust_config(&internal_config) != 0) {
@@ -774,7 +795,10 @@ rte_eal_init(int argc, char **argv)
 
 	thread_id = pthread_self();
 
-	eal_reset_internal_config(&internal_config);
+	if (!run_once_reset_internal_config) {
+		eal_reset_internal_config(&internal_config);
+		run_once_reset_internal_config = 1;
+	}
 
 	/* set log level as early as possible */
 	eal_log_level_parse(argc, argv);
@@ -995,3 +1019,68 @@ rte_eal_check_module(const char *module_name)
 	/* Module has been found */
 	return 1;
 }
+
+#ifdef RTE_LIBRTE_CFGFILE
+int
+rte_eal_configure(struct rte_cfgfile *cfg, char *prgname)
+{
+	int n_entries;
+	int i;
+	int opt;
+	int option_index;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, "DPDK");
+
+	if (n_entries < 1) {
+		printf("No DPDK section entries in cfgfile object\n");
+		return 0;
+	}
+
+	struct rte_cfgfile_entry entries[n_entries];
+
+	if (n_entries !=
+			rte_cfgfile_section_entries(cfg, "DPDK", entries,
+					n_entries)) {
+		rte_eal_init_alert("Unexpected fault.");
+		rte_errno = EFAULT;
+		return -1;
+	}
+
+	if (!run_once_reset_internal_config) {
+		eal_reset_internal_config(&internal_config);
+		run_once_reset_internal_config = 1;
+	}
+
+	/* set log level as early as possible */
+	eal_log_level_cfg(cfg);
+
+	if (rte_eal_cpu_init() < 0) {
+		rte_eal_init_alert("Cannot detect lcores.");
+		rte_errno = ENOTSUP;
+		return -1;
+	}
+
+	for (i = 0; i < n_entries; i++) {
+		eal_getopt(entries[i].name, &opt, &option_index);
+
+		if (eal_parse_option(opt, entries[i].value,
+				option_index, prgname) != 0) {
+			rte_eal_init_alert("Invalid config file arguments.");
+			rte_errno = EINVAL;
+			return -1;
+		}
+	}
+
+	if (parse_vdev_devices(cfg) < 0) {
+		rte_eal_init_alert("Couldn't parse vdevs");
+		rte_errno = ENOMEM;
+		return -1;
+	}
+	return 0;
+}
+#endif
diff --git a/lib/librte_eal/linuxapp/eal/rte_eal_version.map b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
index 0f9e009..d206fd3 100644
--- a/lib/librte_eal/linuxapp/eal/rte_eal_version.map
+++ b/lib/librte_eal/linuxapp/eal/rte_eal_version.map
@@ -205,6 +205,7 @@ DPDK_17.08 {
 	rte_bus_find;
 	rte_bus_find_by_device;
 	rte_bus_find_by_name;
+	rte_eal_configure;
 
 } DPDK_17.05;
 
diff --git a/mk/rte.app.mk b/mk/rte.app.mk
index 5bb4290..b883d08 100644
--- a/mk/rte.app.mk
+++ b/mk/rte.app.mk
@@ -81,7 +81,6 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_POWER)          += -lrte_power
 
 _LDLIBS-$(CONFIG_RTE_LIBRTE_TIMER)          += -lrte_timer
 _LDLIBS-$(CONFIG_RTE_LIBRTE_EFD)            += -lrte_efd
-_LDLIBS-$(CONFIG_RTE_LIBRTE_CFGFILE)        += -lrte_cfgfile
 
 _LDLIBS-y += --whole-archive
 
@@ -97,6 +96,7 @@ _LDLIBS-$(CONFIG_RTE_LIBRTE_MEMPOOL)        += -lrte_mempool
 _LDLIBS-$(CONFIG_RTE_DRIVER_MEMPOOL_RING)   += -lrte_mempool_ring
 _LDLIBS-$(CONFIG_RTE_LIBRTE_RING)           += -lrte_ring
 _LDLIBS-$(CONFIG_RTE_LIBRTE_EAL)            += -lrte_eal
+_LDLIBS-$(CONFIG_RTE_LIBRTE_CFGFILE)        += -lrte_cfgfile
 _LDLIBS-$(CONFIG_RTE_LIBRTE_CMDLINE)        += -lrte_cmdline
 _LDLIBS-$(CONFIG_RTE_LIBRTE_REORDER)        += -lrte_reorder
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v5 2/3] app/testpmd: add parse options from cfg file
  2017-07-13 10:07               ` Kuba Kozak
  2017-07-13 10:07                 ` [PATCH v5 1/3] eal: add functions parsing EAL arguments Kuba Kozak
@ 2017-07-13 10:07                 ` Kuba Kozak
  2017-07-13 10:07                 ` [PATCH v5 3/3] app/testpmd: add parse options from JSON " Kuba Kozak
  2019-01-23 19:31                 ` [PATCH v5 0/3] EAL change for using a config file for DPDK Ferruh Yigit
  3 siblings, 0 replies; 70+ messages in thread
From: Kuba Kozak @ 2017-07-13 10:07 UTC (permalink / raw)
  To: dev
  Cc: deepak.k.jain, bruce.richardson, michalx.k.jastrzebski,
	jacekx.piasecki, Kuba Kozak

This patch shows how to pass arguments to application
using config.ini file.

If a --cfgfile-path <path> option is passed into commandline
non EAL section, then the file is loaded and used by app.

If a config.ini file is present in current working
directory, and no --cfgfile-path option is passed in, config.ini
file will be loaded and used by app as default configuration.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test-pmd/config.ini   |   24 +
 app/test-pmd/parameters.c | 1181 +++++++++++++++++++++++++--------------------
 app/test-pmd/testpmd.c    |   67 ++-
 app/test-pmd/testpmd.h    |    3 +-
 4 files changed, 761 insertions(+), 514 deletions(-)
 create mode 100644 app/test-pmd/config.ini

diff --git a/app/test-pmd/config.ini b/app/test-pmd/config.ini
new file mode 100644
index 0000000..54c83a2
--- /dev/null
+++ b/app/test-pmd/config.ini
@@ -0,0 +1,24 @@
+[DPDK]
+v =
+l = 0-4
+n = 4
+master-lcore = 0
+proc-type = primary
+
+[TEST-PMD]
+i =
+portmask = 0xff
+nb-cores = 4
+port-topology = paired
+
+[DPDK.vdev0]
+net_ring0 =
+
+[DPDK.vdev1]
+net_ring1 =
+
+[DPDK.vdev2]
+net_ring2 =
+
+[DPDK.vdev3]
+net_ring3 =
\ No newline at end of file
diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 958b3d0..d32861a 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -77,9 +77,98 @@
 #include <rte_eth_bond.h>
 #endif
 #include <rte_flow.h>
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#endif
 
 #include "testpmd.h"
 
+enum { TX, RX };
+
+static struct option lgopts[] = {
+	{ "cfgfile-path",		1, 0, 1 },
+	{ "help",			0, 0, 0 },
+#ifdef RTE_LIBRTE_CMDLINE
+	{ "interactive",		0, 0, 0 },
+	{ "cmdline-file",		1, 0, 0 },
+	{ "auto-start",			0, 0, 0 },
+	{ "eth-peers-configfile",	1, 0, 0 },
+	{ "eth-peer",			1, 0, 0 },
+#endif
+	{ "tx-first",			0, 0, 0 },
+	{ "stats-period",		1, 0, 0 },
+	{ "ports",			1, 0, 0 },
+	{ "nb-cores",			1, 0, 0 },
+	{ "nb-ports",			1, 0, 0 },
+	{ "coremask",			1, 0, 0 },
+	{ "portmask",			1, 0, 0 },
+	{ "numa",			0, 0, 0 },
+	{ "no-numa",			0, 0, 0 },
+	{ "mp-anon",			0, 0, 0 },
+	{ "port-numa-config",           1, 0, 0 },
+	{ "ring-numa-config",           1, 0, 0 },
+	{ "socket-num",			1, 0, 0 },
+	{ "mbuf-size",			1, 0, 0 },
+	{ "total-num-mbufs",		1, 0, 0 },
+	{ "max-pkt-len",		1, 0, 0 },
+	{ "pkt-filter-mode",            1, 0, 0 },
+	{ "pkt-filter-report-hash",     1, 0, 0 },
+	{ "pkt-filter-size",            1, 0, 0 },
+	{ "pkt-filter-drop-queue",      1, 0, 0 },
+#ifdef RTE_LIBRTE_LATENCY_STATS
+	{ "latencystats",               1, 0, 0 },
+#endif
+#ifdef RTE_LIBRTE_BITRATE
+	{ "bitrate-stats",              1, 0, 0 },
+#endif
+	{ "disable-crc-strip",          0, 0, 0 },
+	{ "enable-lro",                 0, 0, 0 },
+	{ "enable-rx-cksum",            0, 0, 0 },
+	{ "enable-scatter",             0, 0, 0 },
+	{ "disable-hw-vlan",            0, 0, 0 },
+	{ "disable-hw-vlan-filter",     0, 0, 0 },
+	{ "disable-hw-vlan-strip",      0, 0, 0 },
+	{ "disable-hw-vlan-extend",     0, 0, 0 },
+	{ "enable-drop-en",            0, 0, 0 },
+	{ "disable-rss",                0, 0, 0 },
+	{ "port-topology",              1, 0, 0 },
+	{ "forward-mode",               1, 0, 0 },
+	{ "rss-ip",			0, 0, 0 },
+	{ "rss-udp",			0, 0, 0 },
+	{ "rxq",			1, 0, 0 },
+	{ "txq",			1, 0, 0 },
+	{ "rxd",			1, 0, 0 },
+	{ "txd",			1, 0, 0 },
+	{ "burst",			1, 0, 0 },
+	{ "mbcache",			1, 0, 0 },
+	{ "txpt",			1, 0, 0 },
+	{ "txht",			1, 0, 0 },
+	{ "txwt",			1, 0, 0 },
+	{ "txfreet",			1, 0, 0 },
+	{ "txrst",			1, 0, 0 },
+	{ "txqflags",			1, 0, 0 },
+	{ "rxpt",			1, 0, 0 },
+	{ "rxht",			1, 0, 0 },
+	{ "rxwt",			1, 0, 0 },
+	{ "rxfreet",                    1, 0, 0 },
+	{ "tx-queue-stats-mapping",	1, 0, 0 },
+	{ "rx-queue-stats-mapping",	1, 0, 0 },
+	{ "no-flush-rx",	0, 0, 0 },
+	{ "txpkts",			1, 0, 0 },
+	{ "disable-link-check",		0, 0, 0 },
+	{ "no-lsc-interrupt",		0, 0, 0 },
+	{ "no-rmv-interrupt",		0, 0, 0 },
+	{ "print-event",		1, 0, 0 },
+	{ "mask-event",			1, 0, 0 },
+	{ 0, 0, 0, 0 },
+};
+
+#ifdef RTE_LIBRTE_CMDLINE
+#define SHORTOPTS "i"
+#else
+#define SHORTOPTS ""
+#endif
+
 static void
 usage(char* progname)
 {
@@ -554,578 +643,646 @@ parse_event_printing_config(const char *optarg, int enable)
 	return 0;
 }
 
-void
-launch_args_parse(int argc, char** argv)
+static int
+parse_option(int opt, char *optarg, int opt_idx, char *prgname)
 {
-	int n, opt;
-	char **argvopt;
-	int opt_idx;
-	enum { TX, RX };
+	int n;
 
-	static struct option lgopts[] = {
-		{ "help",			0, 0, 0 },
+	switch (opt) {
 #ifdef RTE_LIBRTE_CMDLINE
-		{ "interactive",		0, 0, 0 },
-		{ "cmdline-file",		1, 0, 0 },
-		{ "auto-start",			0, 0, 0 },
-		{ "eth-peers-configfile",	1, 0, 0 },
-		{ "eth-peer",			1, 0, 0 },
-#endif
-		{ "tx-first",			0, 0, 0 },
-		{ "stats-period",		1, 0, 0 },
-		{ "ports",			1, 0, 0 },
-		{ "nb-cores",			1, 0, 0 },
-		{ "nb-ports",			1, 0, 0 },
-		{ "coremask",			1, 0, 0 },
-		{ "portmask",			1, 0, 0 },
-		{ "numa",			0, 0, 0 },
-		{ "no-numa",			0, 0, 0 },
-		{ "mp-anon",			0, 0, 0 },
-		{ "port-numa-config",           1, 0, 0 },
-		{ "ring-numa-config",           1, 0, 0 },
-		{ "socket-num",			1, 0, 0 },
-		{ "mbuf-size",			1, 0, 0 },
-		{ "total-num-mbufs",		1, 0, 0 },
-		{ "max-pkt-len",		1, 0, 0 },
-		{ "pkt-filter-mode",            1, 0, 0 },
-		{ "pkt-filter-report-hash",     1, 0, 0 },
-		{ "pkt-filter-size",            1, 0, 0 },
-		{ "pkt-filter-drop-queue",      1, 0, 0 },
-#ifdef RTE_LIBRTE_LATENCY_STATS
-		{ "latencystats",               1, 0, 0 },
-#endif
-#ifdef RTE_LIBRTE_BITRATE
-		{ "bitrate-stats",              1, 0, 0 },
+	case 'i':
+		printf("Interactive-mode selected\n");
+		interactive = 1;
+		break;
 #endif
-		{ "disable-crc-strip",          0, 0, 0 },
-		{ "enable-lro",                 0, 0, 0 },
-		{ "enable-rx-cksum",            0, 0, 0 },
-		{ "enable-scatter",             0, 0, 0 },
-		{ "disable-hw-vlan",            0, 0, 0 },
-		{ "disable-hw-vlan-filter",     0, 0, 0 },
-		{ "disable-hw-vlan-strip",      0, 0, 0 },
-		{ "disable-hw-vlan-extend",     0, 0, 0 },
-		{ "enable-drop-en",            0, 0, 0 },
-		{ "disable-rss",                0, 0, 0 },
-		{ "port-topology",              1, 0, 0 },
-		{ "forward-mode",               1, 0, 0 },
-		{ "rss-ip",			0, 0, 0 },
-		{ "rss-udp",			0, 0, 0 },
-		{ "rxq",			1, 0, 0 },
-		{ "txq",			1, 0, 0 },
-		{ "rxd",			1, 0, 0 },
-		{ "txd",			1, 0, 0 },
-		{ "burst",			1, 0, 0 },
-		{ "mbcache",			1, 0, 0 },
-		{ "txpt",			1, 0, 0 },
-		{ "txht",			1, 0, 0 },
-		{ "txwt",			1, 0, 0 },
-		{ "txfreet",			1, 0, 0 },
-		{ "txrst",			1, 0, 0 },
-		{ "txqflags",			1, 0, 0 },
-		{ "rxpt",			1, 0, 0 },
-		{ "rxht",			1, 0, 0 },
-		{ "rxwt",			1, 0, 0 },
-		{ "rxfreet",                    1, 0, 0 },
-		{ "tx-queue-stats-mapping",	1, 0, 0 },
-		{ "rx-queue-stats-mapping",	1, 0, 0 },
-		{ "no-flush-rx",	0, 0, 0 },
-		{ "txpkts",			1, 0, 0 },
-		{ "disable-link-check",		0, 0, 0 },
-		{ "no-lsc-interrupt",		0, 0, 0 },
-		{ "no-rmv-interrupt",		0, 0, 0 },
-		{ "print-event",		1, 0, 0 },
-		{ "mask-event",			1, 0, 0 },
-		{ 0, 0, 0, 0 },
-	};
+	case 'a':
+		printf("Auto-start selected\n");
+		auto_start = 1;
+		break;
 
-	argvopt = argv;
-
-#ifdef RTE_LIBRTE_CMDLINE
-#define SHORTOPTS "i"
-#else
-#define SHORTOPTS ""
-#endif
-	while ((opt = getopt_long(argc, argvopt, SHORTOPTS "ah",
-				 lgopts, &opt_idx)) != EOF) {
-		switch (opt) {
+	case 0: /*long options */
+		if (!strcmp(lgopts[opt_idx].name, "help")) {
+			usage(prgname);
+			rte_exit(EXIT_SUCCESS, "Displayed help\n");
+			return 0;
+		}
 #ifdef RTE_LIBRTE_CMDLINE
-		case 'i':
+		if (!strcmp(lgopts[opt_idx].name, "interactive")) {
 			printf("Interactive-mode selected\n");
 			interactive = 1;
-			break;
-#endif
-		case 'a':
+		}
+		if (!strcmp(lgopts[opt_idx].name, "cmdline-file")) {
+			printf("CLI commands to be read from %s\n",
+			       optarg);
+			snprintf(cmdline_filename,
+				 sizeof(cmdline_filename), "%s",
+				 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "auto-start")) {
 			printf("Auto-start selected\n");
 			auto_start = 1;
-			break;
+		}
+		if (!strcmp(lgopts[opt_idx].name, "tx-first")) {
+			printf("Ports to start sending a burst of "
+					"packets first\n");
+			tx_first = 1;
+		}
+		if (!strcmp(lgopts[opt_idx].name, "stats-period")) {
+			char *end = NULL;
+			unsigned int n;
 
-		case 0: /*long options */
-			if (!strcmp(lgopts[opt_idx].name, "help")) {
-				usage(argv[0]);
-				rte_exit(EXIT_SUCCESS, "Displayed help\n");
-			}
-#ifdef RTE_LIBRTE_CMDLINE
-			if (!strcmp(lgopts[opt_idx].name, "interactive")) {
-				printf("Interactive-mode selected\n");
-				interactive = 1;
-			}
-			if (!strcmp(lgopts[opt_idx].name, "cmdline-file")) {
-				printf("CLI commands to be read from %s\n",
-				       optarg);
-				snprintf(cmdline_filename,
-					 sizeof(cmdline_filename), "%s",
-					 optarg);
-			}
-			if (!strcmp(lgopts[opt_idx].name, "auto-start")) {
-				printf("Auto-start selected\n");
-				auto_start = 1;
-			}
-			if (!strcmp(lgopts[opt_idx].name, "tx-first")) {
-				printf("Ports to start sending a burst of "
-						"packets first\n");
-				tx_first = 1;
-			}
-			if (!strcmp(lgopts[opt_idx].name, "stats-period")) {
-				char *end = NULL;
-				unsigned int n;
+			n = strtoul(optarg, &end, 10);
+			if ((optarg[0] == '\0') || (end == NULL) ||
+					(*end != '\0'))
+				break;
 
-				n = strtoul(optarg, &end, 10);
-				if ((optarg[0] == '\0') || (end == NULL) ||
-						(*end != '\0'))
-					break;
+			stats_period = n;
+			break;
+		}
+		if (!strcmp(lgopts[opt_idx].name,
+			    "eth-peers-configfile")) {
+			if (init_peer_eth_addrs(optarg) != 0)
+				rte_exit(EXIT_FAILURE,
+					 "Cannot open logfile\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "eth-peer")) {
+			char *port_end;
+			uint8_t c, peer_addr[6];
 
-				stats_period = n;
-				break;
-			}
-			if (!strcmp(lgopts[opt_idx].name,
-				    "eth-peers-configfile")) {
-				if (init_peer_eth_addrs(optarg) != 0)
-					rte_exit(EXIT_FAILURE,
-						 "Cannot open logfile\n");
+			errno = 0;
+			n = strtoul(optarg, &port_end, 10);
+			if (errno != 0 || port_end == optarg ||
+					*port_end++ != ',')
+				rte_exit(EXIT_FAILURE,
+					 "Invalid eth-peer: %s", optarg);
+			if (n >= RTE_MAX_ETHPORTS) {
+				rte_exit(EXIT_FAILURE,
+					 "eth-peer: port %d >= "
+					 "RTE_MAX_ETHPORTS(%d)\n",
+					 n, RTE_MAX_ETHPORTS);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "eth-peer")) {
-				char *port_end;
-				uint8_t c, peer_addr[6];
-
-				errno = 0;
-				n = strtoul(optarg, &port_end, 10);
-				if (errno != 0 || port_end == optarg || *port_end++ != ',')
-					rte_exit(EXIT_FAILURE,
-						 "Invalid eth-peer: %s", optarg);
-				if (n >= RTE_MAX_ETHPORTS)
-					rte_exit(EXIT_FAILURE,
-						 "eth-peer: port %d >= RTE_MAX_ETHPORTS(%d)\n",
-						 n, RTE_MAX_ETHPORTS);
 
-				if (cmdline_parse_etheraddr(NULL, port_end,
-						&peer_addr, sizeof(peer_addr)) < 0)
-					rte_exit(EXIT_FAILURE,
-						 "Invalid ethernet address: %s\n",
-						 port_end);
-				for (c = 0; c < 6; c++)
-					peer_eth_addrs[n].addr_bytes[c] =
-						peer_addr[c];
-				nb_peer_eth_addrs++;
+			if (cmdline_parse_etheraddr(NULL, port_end,
+					&peer_addr, sizeof(peer_addr)) < 0) {
+				rte_exit(EXIT_FAILURE,
+					 "Invalid ethernet address: %s\n",
+					 port_end);
+				return -1;
 			}
+			for (c = 0; c < 6; c++)
+				peer_eth_addrs[n].addr_bytes[c] =
+					peer_addr[c];
+			nb_peer_eth_addrs++;
+		}
 #endif
-			if (!strcmp(lgopts[opt_idx].name, "nb-ports")) {
-				n = atoi(optarg);
-				if (n > 0 && n <= nb_ports)
-					nb_fwd_ports = (uint8_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "Invalid port %d\n", n);
+		if (!strcmp(lgopts[opt_idx].name, "nb-ports")) {
+			n = atoi(optarg);
+			if (n > 0 && n <= nb_ports)
+				nb_fwd_ports = (uint8_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "Invalid port %d\n", n);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "nb-cores")) {
-				n = atoi(optarg);
-				if (n > 0 && n <= nb_lcores)
-					nb_fwd_lcores = (uint8_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "nb-cores should be > 0 and <= %d\n",
-						 nb_lcores);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "nb-cores")) {
+			n = atoi(optarg);
+			if (n > 0 && n <= nb_lcores)
+				nb_fwd_lcores = (uint8_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "nb-cores should be > 0 and <= %d\n",
+					 nb_lcores);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "coremask"))
-				parse_fwd_coremask(optarg);
-			if (!strcmp(lgopts[opt_idx].name, "portmask"))
-				parse_fwd_portmask(optarg);
-			if (!strcmp(lgopts[opt_idx].name, "no-numa"))
-				numa_support = 0;
-			if (!strcmp(lgopts[opt_idx].name, "numa"))
-				numa_support = 1;
-			if (!strcmp(lgopts[opt_idx].name, "mp-anon")) {
-				mp_anon = 1;
+		}
+		if (!strcmp(lgopts[opt_idx].name, "coremask"))
+			parse_fwd_coremask(optarg);
+		if (!strcmp(lgopts[opt_idx].name, "portmask"))
+			parse_fwd_portmask(optarg);
+		if (!strcmp(lgopts[opt_idx].name, "no-numa"))
+			numa_support = 0;
+		if (!strcmp(lgopts[opt_idx].name, "numa"))
+			numa_support = 1;
+		if (!strcmp(lgopts[opt_idx].name, "mp-anon"))
+			mp_anon = 1;
+		if (!strcmp(lgopts[opt_idx].name, "port-numa-config")) {
+			if (parse_portnuma_config(optarg)) {
+				rte_exit(EXIT_FAILURE,
+				   "invalid port-numa configuration\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "port-numa-config")) {
-				if (parse_portnuma_config(optarg))
-					rte_exit(EXIT_FAILURE,
-					   "invalid port-numa configuration\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "ring-numa-config"))
+			if (parse_ringnuma_config(optarg)) {
+				rte_exit(EXIT_FAILURE,
+				   "invalid ring-numa configuration\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "ring-numa-config"))
-				if (parse_ringnuma_config(optarg))
-					rte_exit(EXIT_FAILURE,
-					   "invalid ring-numa configuration\n");
-			if (!strcmp(lgopts[opt_idx].name, "socket-num")) {
-				n = atoi(optarg);
-				if (!new_socket_id((uint8_t)n)) {
-					socket_num = (uint8_t)n;
-				} else {
-					print_invalid_socket_id_error();
-					rte_exit(EXIT_FAILURE,
-						"Invalid socket id");
-				}
+		if (!strcmp(lgopts[opt_idx].name, "socket-num")) {
+			n = atoi(optarg);
+			if (!new_socket_id((uint8_t)n)) {
+				socket_num = (uint8_t)n;
+			} else {
+				print_invalid_socket_id_error();
+				rte_exit(EXIT_FAILURE,
+					"Invalid socket id");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "mbuf-size")) {
-				n = atoi(optarg);
-				if (n > 0 && n <= 0xFFFF)
-					mbuf_data_size = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "mbuf-size should be > 0 and < 65536\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "mbuf-size")) {
+			n = atoi(optarg);
+			if (n > 0 && n <= 0xFFFF)
+				mbuf_data_size = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "mbuf-size should be > 0 and < 65536\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "total-num-mbufs")) {
-				n = atoi(optarg);
-				if (n > 1024)
-					param_total_num_mbufs = (unsigned)n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "total-num-mbufs should be > 1024\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "total-num-mbufs")) {
+			n = atoi(optarg);
+			if (n > 1024)
+				param_total_num_mbufs = (unsigned int)n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "total-num-mbufs should be > 1024\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "max-pkt-len")) {
-				n = atoi(optarg);
-				if (n >= ETHER_MIN_LEN) {
-					rx_mode.max_rx_pkt_len = (uint32_t) n;
-					if (n > ETHER_MAX_LEN)
-					    rx_mode.jumbo_frame = 1;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "Invalid max-pkt-len=%d - should be > %d\n",
-						 n, ETHER_MIN_LEN);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "max-pkt-len")) {
+			n = atoi(optarg);
+			if (n >= ETHER_MIN_LEN) {
+				rx_mode.max_rx_pkt_len = (uint32_t) n;
+				if (n > ETHER_MAX_LEN)
+					rx_mode.jumbo_frame = 1;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "Invalid max-pkt-len=%d - should be > %d\n",
+					 n, ETHER_MIN_LEN);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "pkt-filter-mode")) {
-				if (!strcmp(optarg, "signature"))
-					fdir_conf.mode =
-						RTE_FDIR_MODE_SIGNATURE;
-				else if (!strcmp(optarg, "perfect"))
-					fdir_conf.mode = RTE_FDIR_MODE_PERFECT;
-				else if (!strcmp(optarg, "perfect-mac-vlan"))
-					fdir_conf.mode = RTE_FDIR_MODE_PERFECT_MAC_VLAN;
-				else if (!strcmp(optarg, "perfect-tunnel"))
-					fdir_conf.mode = RTE_FDIR_MODE_PERFECT_TUNNEL;
-				else if (!strcmp(optarg, "none"))
-					fdir_conf.mode = RTE_FDIR_MODE_NONE;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "pkt-mode-invalid %s invalid - must be: "
-						 "none, signature, perfect, perfect-mac-vlan"
-						 " or perfect-tunnel\n",
-						 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "pkt-filter-mode")) {
+			if (!strcmp(optarg, "signature"))
+				fdir_conf.mode =
+					RTE_FDIR_MODE_SIGNATURE;
+			else if (!strcmp(optarg, "perfect"))
+				fdir_conf.mode = RTE_FDIR_MODE_PERFECT;
+			else if (!strcmp(optarg, "perfect-mac-vlan"))
+				fdir_conf.mode = RTE_FDIR_MODE_PERFECT_MAC_VLAN;
+			else if (!strcmp(optarg, "perfect-tunnel"))
+				fdir_conf.mode = RTE_FDIR_MODE_PERFECT_TUNNEL;
+			else if (!strcmp(optarg, "none"))
+				fdir_conf.mode = RTE_FDIR_MODE_NONE;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "pkt-mode-invalid %s invalid - must be: "
+					 "none, signature, perfect, perfect-mac-vlan"
+					 " or perfect-tunnel\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name,
-				    "pkt-filter-report-hash")) {
-				if (!strcmp(optarg, "none"))
-					fdir_conf.status =
-						RTE_FDIR_NO_REPORT_STATUS;
-				else if (!strcmp(optarg, "match"))
-					fdir_conf.status =
-						RTE_FDIR_REPORT_STATUS;
-				else if (!strcmp(optarg, "always"))
-					fdir_conf.status =
-						RTE_FDIR_REPORT_STATUS_ALWAYS;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "pkt-filter-report-hash %s invalid "
-						 "- must be: none or match or always\n",
-						 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name,
+			    "pkt-filter-report-hash")) {
+			if (!strcmp(optarg, "none"))
+				fdir_conf.status =
+					RTE_FDIR_NO_REPORT_STATUS;
+			else if (!strcmp(optarg, "match"))
+				fdir_conf.status =
+					RTE_FDIR_REPORT_STATUS;
+			else if (!strcmp(optarg, "always"))
+				fdir_conf.status =
+					RTE_FDIR_REPORT_STATUS_ALWAYS;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "pkt-filter-report-hash %s invalid "
+					 "- must be: none or match or always\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "pkt-filter-size")) {
-				if (!strcmp(optarg, "64K"))
-					fdir_conf.pballoc =
-						RTE_FDIR_PBALLOC_64K;
-				else if (!strcmp(optarg, "128K"))
-					fdir_conf.pballoc =
-						RTE_FDIR_PBALLOC_128K;
-				else if (!strcmp(optarg, "256K"))
-					fdir_conf.pballoc =
-						RTE_FDIR_PBALLOC_256K;
-				else
-					rte_exit(EXIT_FAILURE, "pkt-filter-size %s invalid -"
-						 " must be: 64K or 128K or 256K\n",
-						 optarg);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "pkt-filter-size")) {
+			if (!strcmp(optarg, "64K"))
+				fdir_conf.pballoc =
+					RTE_FDIR_PBALLOC_64K;
+			else if (!strcmp(optarg, "128K"))
+				fdir_conf.pballoc =
+					RTE_FDIR_PBALLOC_128K;
+			else if (!strcmp(optarg, "256K"))
+				fdir_conf.pballoc =
+					RTE_FDIR_PBALLOC_256K;
+			else {
+				rte_exit(EXIT_FAILURE, "pkt-filter-size %s invalid -"
+					 " must be: 64K or 128K or 256K\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name,
-				    "pkt-filter-drop-queue")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					fdir_conf.drop_queue = (uint8_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "drop queue %d invalid - must"
-						 "be >= 0 \n", n);
+		}
+		if (!strcmp(lgopts[opt_idx].name,
+			    "pkt-filter-drop-queue")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				fdir_conf.drop_queue = (uint8_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "drop queue %d invalid - must "
+					 "be >= 0\n", n);
+				return -1;
 			}
+		}
 #ifdef RTE_LIBRTE_LATENCY_STATS
-			if (!strcmp(lgopts[opt_idx].name,
-				    "latencystats")) {
-				n = atoi(optarg);
-				if (n >= 0) {
-					latencystats_lcore_id = (lcoreid_t) n;
-					latencystats_enabled = 1;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "invalid lcore id %d for latencystats"
-						 " must be >= 0\n", n);
+		if (!strcmp(lgopts[opt_idx].name,
+			    "latencystats")) {
+			n = atoi(optarg);
+			if (n >= 0) {
+				latencystats_lcore_id = (lcoreid_t) n;
+				latencystats_enabled = 1;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "invalid lcore id %d for latencystats"
+					 " must be >= 0\n", n);
+				return -1;
 			}
+		}
 #endif
 #ifdef RTE_LIBRTE_BITRATE
-			if (!strcmp(lgopts[opt_idx].name, "bitrate-stats")) {
-				n = atoi(optarg);
-				if (n >= 0) {
-					bitrate_lcore_id = (lcoreid_t) n;
-					bitrate_enabled = 1;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "invalid lcore id %d for bitrate stats"
-						 " must be >= 0\n", n);
+		if (!strcmp(lgopts[opt_idx].name, "bitrate-stats")) {
+			n = atoi(optarg);
+			if (n >= 0) {
+				bitrate_lcore_id = (lcoreid_t) n;
+				bitrate_enabled = 1;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "invalid lcore id %d for bitrate stats"
+					 " must be >= 0\n", n);
+				return -1;
 			}
+		}
 #endif
-			if (!strcmp(lgopts[opt_idx].name, "disable-crc-strip"))
-				rx_mode.hw_strip_crc = 0;
-			if (!strcmp(lgopts[opt_idx].name, "enable-lro"))
-				rx_mode.enable_lro = 1;
-			if (!strcmp(lgopts[opt_idx].name, "enable-scatter"))
-				rx_mode.enable_scatter = 1;
-			if (!strcmp(lgopts[opt_idx].name, "enable-rx-cksum"))
-				rx_mode.hw_ip_checksum = 1;
-
-			if (!strcmp(lgopts[opt_idx].name, "disable-hw-vlan")) {
-				rx_mode.hw_vlan_filter = 0;
-				rx_mode.hw_vlan_strip  = 0;
-				rx_mode.hw_vlan_extend = 0;
-			}
+		if (!strcmp(lgopts[opt_idx].name, "disable-crc-strip"))
+			rx_mode.hw_strip_crc = 0;
+		if (!strcmp(lgopts[opt_idx].name, "enable-lro"))
+			rx_mode.enable_lro = 1;
+		if (!strcmp(lgopts[opt_idx].name, "enable-scatter"))
+			rx_mode.enable_scatter = 1;
+		if (!strcmp(lgopts[opt_idx].name, "enable-rx-cksum"))
+			rx_mode.hw_ip_checksum = 1;
 
-			if (!strcmp(lgopts[opt_idx].name,
-					"disable-hw-vlan-filter"))
-				rx_mode.hw_vlan_filter = 0;
-
-			if (!strcmp(lgopts[opt_idx].name,
-					"disable-hw-vlan-strip"))
-				rx_mode.hw_vlan_strip  = 0;
-
-			if (!strcmp(lgopts[opt_idx].name,
-					"disable-hw-vlan-extend"))
-				rx_mode.hw_vlan_extend = 0;
-
-			if (!strcmp(lgopts[opt_idx].name, "enable-drop-en"))
-				rx_drop_en = 1;
-
-			if (!strcmp(lgopts[opt_idx].name, "disable-rss"))
-				rss_hf = 0;
-			if (!strcmp(lgopts[opt_idx].name, "port-topology")) {
-				if (!strcmp(optarg, "paired"))
-					port_topology = PORT_TOPOLOGY_PAIRED;
-				else if (!strcmp(optarg, "chained"))
-					port_topology = PORT_TOPOLOGY_CHAINED;
-				else if (!strcmp(optarg, "loop"))
-					port_topology = PORT_TOPOLOGY_LOOP;
-				else
-					rte_exit(EXIT_FAILURE, "port-topology %s invalid -"
-						 " must be: paired or chained \n",
-						 optarg);
+		if (!strcmp(lgopts[opt_idx].name, "disable-hw-vlan")) {
+			rx_mode.hw_vlan_filter = 0;
+			rx_mode.hw_vlan_strip  = 0;
+			rx_mode.hw_vlan_extend = 0;
+		}
+
+		if (!strcmp(lgopts[opt_idx].name,
+				"disable-hw-vlan-filter"))
+			rx_mode.hw_vlan_filter = 0;
+
+		if (!strcmp(lgopts[opt_idx].name,
+				"disable-hw-vlan-strip"))
+			rx_mode.hw_vlan_strip  = 0;
+
+		if (!strcmp(lgopts[opt_idx].name,
+				"disable-hw-vlan-extend"))
+			rx_mode.hw_vlan_extend = 0;
+
+		if (!strcmp(lgopts[opt_idx].name, "enable-drop-en"))
+			rx_drop_en = 1;
+
+		if (!strcmp(lgopts[opt_idx].name, "disable-rss"))
+			rss_hf = 0;
+		if (!strcmp(lgopts[opt_idx].name, "port-topology")) {
+			if (!strcmp(optarg, "paired"))
+				port_topology = PORT_TOPOLOGY_PAIRED;
+			else if (!strcmp(optarg, "chained"))
+				port_topology = PORT_TOPOLOGY_CHAINED;
+			else if (!strcmp(optarg, "loop"))
+				port_topology = PORT_TOPOLOGY_LOOP;
+			else {
+				rte_exit(EXIT_FAILURE, "port-topology %s invalid -"
+					 " must be: paired or chained\n",
+					 optarg);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "forward-mode"))
-				set_pkt_forwarding_mode(optarg);
-			if (!strcmp(lgopts[opt_idx].name, "rss-ip"))
-				rss_hf = ETH_RSS_IP;
-			if (!strcmp(lgopts[opt_idx].name, "rss-udp"))
-				rss_hf = ETH_RSS_UDP;
-			if (!strcmp(lgopts[opt_idx].name, "rxq")) {
-				n = atoi(optarg);
-				if (n >= 0 && n <= (int) MAX_QUEUE_ID)
-					nb_rxq = (queueid_t) n;
-				else
-					rte_exit(EXIT_FAILURE, "rxq %d invalid - must be"
-						  " >= 0 && <= %d\n", n,
-						  (int) MAX_QUEUE_ID);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "forward-mode"))
+			set_pkt_forwarding_mode(optarg);
+		if (!strcmp(lgopts[opt_idx].name, "rss-ip"))
+			rss_hf = ETH_RSS_IP;
+		if (!strcmp(lgopts[opt_idx].name, "rss-udp"))
+			rss_hf = ETH_RSS_UDP;
+		if (!strcmp(lgopts[opt_idx].name, "rxq")) {
+			n = atoi(optarg);
+			if (n >= 0 && n <= (int) MAX_QUEUE_ID)
+				nb_rxq = (queueid_t) n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxq %d invalid - must be"
+					  " >= 0 && <= %d\n", n,
+					  (int) MAX_QUEUE_ID);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txq")) {
-				n = atoi(optarg);
-				if (n >= 0 && n <= (int) MAX_QUEUE_ID)
-					nb_txq = (queueid_t) n;
-				else
-					rte_exit(EXIT_FAILURE, "txq %d invalid - must be"
-						  " >= 0 && <= %d\n", n,
-						  (int) MAX_QUEUE_ID);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txq")) {
+			n = atoi(optarg);
+			if (n >= 0 && n <= (int) MAX_QUEUE_ID)
+				nb_txq = (queueid_t) n;
+			else {
+				rte_exit(EXIT_FAILURE, "txq %d invalid - must be"
+					  " >= 0 && <= %d\n", n,
+					  (int) MAX_QUEUE_ID);
+				return -1;
 			}
-			if (!nb_rxq && !nb_txq) {
-				rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
-						"be non-zero\n");
+		}
+		if (!nb_rxq && !nb_txq) {
+			rte_exit(EXIT_FAILURE, "Either rx or tx queues should "
+					"be non-zero\n");
+			return -1;
+		}
+		if (!strcmp(lgopts[opt_idx].name, "burst")) {
+			n = atoi(optarg);
+			if ((n >= 1) && (n <= MAX_PKT_BURST))
+				nb_pkt_per_burst = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "burst must >= 1 and <= %d]",
+					 MAX_PKT_BURST);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "burst")) {
-				n = atoi(optarg);
-				if ((n >= 1) && (n <= MAX_PKT_BURST))
-					nb_pkt_per_burst = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "burst must >= 1 and <= %d]",
-						 MAX_PKT_BURST);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "mbcache")) {
+			n = atoi(optarg);
+			if ((n >= 0) &&
+			    (n <= RTE_MEMPOOL_CACHE_MAX_SIZE))
+				mb_mempool_cache = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "mbcache must be >= 0 and <= %d\n",
+					 RTE_MEMPOOL_CACHE_MAX_SIZE);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "mbcache")) {
-				n = atoi(optarg);
-				if ((n >= 0) &&
-				    (n <= RTE_MEMPOOL_CACHE_MAX_SIZE))
-					mb_mempool_cache = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE,
-						 "mbcache must be >= 0 and <= %d\n",
-						 RTE_MEMPOOL_CACHE_MAX_SIZE);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txfreet")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_free_thresh = (int16_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txfreet must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txfreet")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_free_thresh = (int16_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txfreet must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txrst")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_rs_thresh = (int16_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txrst must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txrst")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_rs_thresh = (int16_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txrst must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txqflags")) {
+			char *end = NULL;
+
+			n = strtoul(optarg, &end, 16);
+			if (n >= 0)
+				txq_flags = (int32_t)n;
+			else {
+				rte_exit(EXIT_FAILURE,
+					 "txqflags must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txqflags")) {
-				char *end = NULL;
-				n = strtoul(optarg, &end, 16);
-				if (n >= 0)
-					txq_flags = (int32_t)n;
-				else
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxd")) {
+			n = atoi(optarg);
+			if (n > 0) {
+				if (rx_free_thresh >= n)
 					rte_exit(EXIT_FAILURE,
-						 "txqflags must be >= 0\n");
+						 "rxd must be > "
+						 "rx_free_thresh(%d)\n",
+						 (int)rx_free_thresh);
+				else
+					nb_rxd = (uint16_t) n;
+			} else {
+				rte_exit(EXIT_FAILURE,
+					 "rxd(%d) invalid - must be > 0\n",
+					 n);
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxd")) {
-				n = atoi(optarg);
-				if (n > 0) {
-					if (rx_free_thresh >= n)
-						rte_exit(EXIT_FAILURE,
-							 "rxd must be > "
-							 "rx_free_thresh(%d)\n",
-							 (int)rx_free_thresh);
-					else
-						nb_rxd = (uint16_t) n;
-				} else
-					rte_exit(EXIT_FAILURE,
-						 "rxd(%d) invalid - must be > 0\n",
-						 n);
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txd")) {
+			n = atoi(optarg);
+			if (n > 0)
+				nb_txd = (uint16_t) n;
+			else {
+				rte_exit(EXIT_FAILURE, "txd must be in > 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txd")) {
-				n = atoi(optarg);
-				if (n > 0)
-					nb_txd = (uint16_t) n;
-				else
-					rte_exit(EXIT_FAILURE, "txd must be in > 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txpt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_pthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txpt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txpt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_pthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txpt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txht")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_hthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txht must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txht")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_hthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txht must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txwt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				tx_wthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "txwt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txwt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					tx_wthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "txwt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxpt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_pthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxpt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxpt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_pthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxpt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxht")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_hthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxht must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxht")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_hthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxht must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxwt")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_wthresh = (int8_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxwt must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxwt")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_wthresh = (int8_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxwt must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rxfreet")) {
+			n = atoi(optarg);
+			if (n >= 0)
+				rx_free_thresh = (int16_t)n;
+			else {
+				rte_exit(EXIT_FAILURE, "rxfreet must be >= 0\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rxfreet")) {
-				n = atoi(optarg);
-				if (n >= 0)
-					rx_free_thresh = (int16_t)n;
-				else
-					rte_exit(EXIT_FAILURE, "rxfreet must be >= 0\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "tx-queue-stats-mapping")) {
+			if (parse_queue_stats_mapping_config(optarg, TX)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid TX queue statistics mapping config entered\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "tx-queue-stats-mapping")) {
-				if (parse_queue_stats_mapping_config(optarg, TX)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid TX queue statistics mapping config entered\n");
-				}
+		}
+		if (!strcmp(lgopts[opt_idx].name, "rx-queue-stats-mapping")) {
+			if (parse_queue_stats_mapping_config(optarg, RX)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid RX queue statistics mapping config entered\n");
 			}
-			if (!strcmp(lgopts[opt_idx].name, "rx-queue-stats-mapping")) {
-				if (parse_queue_stats_mapping_config(optarg, RX)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid RX queue statistics mapping config entered\n");
-				}
+		}
+		if (!strcmp(lgopts[opt_idx].name, "txpkts")) {
+			unsigned int seg_lengths[RTE_MAX_SEGS_PER_PKT];
+			unsigned int nb_segs;
+
+			nb_segs = parse_item_list(optarg, "txpkt segments",
+					RTE_MAX_SEGS_PER_PKT, seg_lengths, 0);
+			if (nb_segs > 0)
+				set_tx_pkt_segments(seg_lengths, nb_segs);
+			else {
+				rte_exit(EXIT_FAILURE, "bad txpkts\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "txpkts")) {
-				unsigned seg_lengths[RTE_MAX_SEGS_PER_PKT];
-				unsigned int nb_segs;
-
-				nb_segs = parse_item_list(optarg, "txpkt segments",
-						RTE_MAX_SEGS_PER_PKT, seg_lengths, 0);
-				if (nb_segs > 0)
-					set_tx_pkt_segments(seg_lengths, nb_segs);
-				else
-					rte_exit(EXIT_FAILURE, "bad txpkts\n");
+		}
+		if (!strcmp(lgopts[opt_idx].name, "no-flush-rx"))
+			no_flush_rx = 1;
+		if (!strcmp(lgopts[opt_idx].name, "disable-link-check"))
+			no_link_check = 1;
+		if (!strcmp(lgopts[opt_idx].name, "no-lsc-interrupt"))
+			lsc_interrupt = 0;
+		if (!strcmp(lgopts[opt_idx].name, "no-rmv-interrupt"))
+			rmv_interrupt = 0;
+		if (!strcmp(lgopts[opt_idx].name, "print-event"))
+			if (parse_event_printing_config(optarg, 1)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid print-event argument\n");
+				return -1;
+			}
+		if (!strcmp(lgopts[opt_idx].name, "mask-event"))
+			if (parse_event_printing_config(optarg, 0)) {
+				rte_exit(EXIT_FAILURE,
+					 "invalid mask-event argument\n");
+				return -1;
 			}
-			if (!strcmp(lgopts[opt_idx].name, "no-flush-rx"))
-				no_flush_rx = 1;
-			if (!strcmp(lgopts[opt_idx].name, "disable-link-check"))
-				no_link_check = 1;
-			if (!strcmp(lgopts[opt_idx].name, "no-lsc-interrupt"))
-				lsc_interrupt = 0;
-			if (!strcmp(lgopts[opt_idx].name, "no-rmv-interrupt"))
-				rmv_interrupt = 0;
-			if (!strcmp(lgopts[opt_idx].name, "print-event"))
-				if (parse_event_printing_config(optarg, 1)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid print-event argument\n");
-				}
-			if (!strcmp(lgopts[opt_idx].name, "mask-event"))
-				if (parse_event_printing_config(optarg, 0)) {
-					rte_exit(EXIT_FAILURE,
-						 "invalid mask-event argument\n");
-				}
 
-			break;
-		case 'h':
-			usage(argv[0]);
-			rte_exit(EXIT_SUCCESS, "Displayed help\n");
-			break;
-		default:
-			usage(argv[0]);
-			rte_exit(EXIT_FAILURE,
-				 "Command line is incomplete or incorrect\n");
+		break;
+	case 'h':
+		usage(prgname);
+		rte_exit(EXIT_SUCCESS, "Displayed help\n");
+		return -1;
+	case 1:
+		/* does nothing*/
+		break;
+	default:
+		usage(prgname);
+		rte_exit(EXIT_FAILURE,
+			 "Command line is incomplete or incorrect\n");
+		return -1;
+	}
+	return 0;
+}
+
+void
+launch_args_parse(int argc, char **argv)
+{
+	int opt;
+	char **argvopt;
+	int opt_idx;
+
+	argvopt = argv;
+
+	while ((opt = getopt_long(argc, argvopt, SHORTOPTS "ah",
+				 lgopts, &opt_idx)) != EOF) {
+		parse_option(opt, optarg, opt_idx, argv[0]);
+	}
+}
+
+#ifdef RTE_LIBRTE_CFGFILE
+static void
+non_eal_getopt(const char *str, int *opt, int *option_index)
+{
+	int i;
+
+	*opt = '?';
+	*option_index = 0;
+
+	if (strlen(str) == 1) {
+		*opt = *str;
+		return;
+	}
+
+	for (i = 0; lgopts[i].name != NULL; i++) {
+		if (strcmp(str, lgopts[i].name) == 0) {
+			*opt = lgopts[i].val;
+			*option_index = i;
 			break;
 		}
 	}
 }
+#endif
+
+#ifdef RTE_LIBRTE_CFGFILE
+#define APP_SECTION "TEST-PMD"
+int non_eal_configure(struct rte_cfgfile *cfg, char *prgname)
+{
+	int n_entries;
+	int i;
+	int opt;
+	int option_index;
+
+	if (cfg == NULL) {
+		rte_errno = -EINVAL;
+		return -1;
+	}
+
+	n_entries = rte_cfgfile_section_num_entries(cfg, APP_SECTION);
+
+	if (n_entries < 1) {
+		printf("No %s section entries "
+				"in cfgfile object\n", APP_SECTION);
+		return -1;
+	}
+
+	struct rte_cfgfile_entry entries[n_entries];
+
+	if (n_entries !=
+		rte_cfgfile_section_entries(cfg, APP_SECTION, entries,
+				n_entries)) {
+		rte_exit(EXIT_FAILURE, "Unexpected fault");
+		return -1;
+	}
+
+	for (i = 0; i < n_entries; i++) {
+		non_eal_getopt(entries[i].name, &opt, &option_index);
+
+		parse_option(opt, entries[i].value,
+				option_index, prgname);
+	}
+	return 0;
+}
+#endif
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index e09b803..7b82976 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -91,6 +91,9 @@
 #include <rte_latencystats.h>
 #endif
 #include <rte_gro.h>
+#ifdef RTE_LIBRTE_CFGFILE
+#include <rte_cfgfile.h>
+#endif
 
 #include "testpmd.h"
 
@@ -2266,15 +2269,66 @@ signal_handler(int signum)
 	}
 }
 
+#ifdef RTE_LIBRTE_CFGFILE
+/* Load config file path from command line */
+static char *
+cfgfile_load_path(int argc, char **argv)
+{
+	int i;
+
+	for (i = 0; i < argc; i++) {
+		if (!strcmp("--cfgfile-path", argv[i])) {
+			if (argv[i+1] && argv[i+1][0] != '-')
+				return strdup(argv[i+1]);
+		} else if (!strncmp("--cfgfile-path=", argv[i], 15)) {
+			char *ptr = argv[i];
+
+			ptr += 15;
+			if (strlen(ptr))
+				return strdup(ptr);
+		}
+	}
+	return NULL;
+}
+#endif
+
+#define APP_NAME "TEST-PMD"
 int
 main(int argc, char** argv)
 {
 	int  diag;
 	uint8_t port_id;
+#ifdef RTE_LIBRTE_CFGFILE
+	struct rte_cfgfile *cfg = NULL;
+	char *config_file = NULL;
+#endif
 
 	signal(SIGINT, signal_handler);
 	signal(SIGTERM, signal_handler);
 
+#ifdef RTE_LIBRTE_CFGFILE
+	/* load --cfgfile-path argument from argv */
+	config_file = cfgfile_load_path(argc, argv);
+
+	if (config_file) {
+		printf("Info: found cfgfile-path \"%s\"\n", config_file);
+	} else {
+		printf("Info: not found cfgfile-path parameter "
+				"(searching for cfgfile "
+				"in default directory)\n");
+		config_file = strdup("config.ini");
+	}
+
+	cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+
+	if (cfg == NULL) {
+		printf("Info: Valid cfgfile not found\n");
+	} else {
+		diag = rte_eal_configure(cfg, argv[0]);
+		if (diag < 0)
+			rte_panic("Cannot init EAL\n");
+	}
+#endif
 	diag = rte_eal_init(argc, argv);
 	if (diag < 0)
 		rte_panic("Cannot init EAL\n");
@@ -2304,6 +2358,18 @@ main(int argc, char** argv)
 	latencystats_enabled = 0;
 #endif
 
+#ifdef RTE_LIBRTE_CFGFILE
+	if (cfg != NULL) {
+		non_eal_configure(cfg, argv[0]);
+		rte_cfgfile_close(cfg);
+		cfg = 0;
+
+		if (config_file) {
+			free(config_file);
+			config_file = 0;
+		}
+	}
+#endif
 	argc -= diag;
 	argv += diag;
 	if (argc > 1)
@@ -2400,6 +2466,5 @@ main(int argc, char** argv)
 		if (rc < 0)
 			return 1;
 	}
-
 	return 0;
 }
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 73985c3..8ff4d88 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -512,7 +512,7 @@ port_pci_reg_write(struct rte_port *port, uint32_t reg_off, uint32_t reg_v)
 unsigned int parse_item_list(char* str, const char* item_name,
 			unsigned int max_items,
 			unsigned int *parsed_items, int check_unique_values);
-void launch_args_parse(int argc, char** argv);
+void launch_args_parse(int argc, char **argv);
 void cmdline_read_from_file(const char *filename);
 void prompt(void);
 void prompt_exit(void);
@@ -656,6 +656,7 @@ enum print_warning {
 };
 int port_id_is_invalid(portid_t port_id, enum print_warning warning);
 int new_socket_id(unsigned int socket_id);
+int non_eal_configure(struct rte_cfgfile *cfg, char *prgname);
 
 /*
  * Work-around of a compilation error with ICC on invocations of the
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* [PATCH v5 3/3] app/testpmd: add parse options from JSON cfg file
  2017-07-13 10:07               ` Kuba Kozak
  2017-07-13 10:07                 ` [PATCH v5 1/3] eal: add functions parsing EAL arguments Kuba Kozak
  2017-07-13 10:07                 ` [PATCH v5 2/3] app/testpmd: add parse options from cfg file Kuba Kozak
@ 2017-07-13 10:07                 ` Kuba Kozak
  2019-01-23 19:31                 ` [PATCH v5 0/3] EAL change for using a config file for DPDK Ferruh Yigit
  3 siblings, 0 replies; 70+ messages in thread
From: Kuba Kozak @ 2017-07-13 10:07 UTC (permalink / raw)
  To: dev
  Cc: deepak.k.jain, bruce.richardson, michalx.k.jastrzebski,
	jacekx.piasecki, Kuba Kozak

This patch shows usage of Jansson library to parse
application arguments from JSON config file.

https://github.com/akheron/jansson

If a --cfgfile-path <path> option is passed into commandline
non EAL section, then the disired JSON file is loaded and used
by app. In case when JSON doesn't exist an INI file is loaded.
The INI file can be passed also from --cfgfile-path option.

If a file called config.ini is present in current working
directory, and no --cfgfile-path option is passed in, config.ini
file will be loaded and used by app.

Signed-off-by: Kuba Kozak <kubax.kozak@intel.com>
Suggested-by: Bruce Richardson <bruce.richardson@intel.com>
---
 app/test-pmd/Makefile    |  6 ++++
 app/test-pmd/config.json | 33 +++++++++++++++++
 app/test-pmd/testpmd.c   | 94 +++++++++++++++++++++++++++++++++++++++++++++++-
 config/common_base       |  5 +++
 4 files changed, 137 insertions(+), 1 deletion(-)
 create mode 100644 app/test-pmd/config.json

diff --git a/app/test-pmd/Makefile b/app/test-pmd/Makefile
index c36be19..a1c84cc 100644
--- a/app/test-pmd/Makefile
+++ b/app/test-pmd/Makefile
@@ -83,6 +83,12 @@ endif
 
 endif
 
+ifeq ($(CONFIG_RTE_JSON_SUPPORT),y)
+ifeq ($(CONFIG_RTE_LIBRTE_CFGFILE),y)
+LDLIBS += -ljansson
+endif
+endif
+
 CFLAGS_cmdline.o := -D_GNU_SOURCE
 
 include $(RTE_SDK)/mk/rte.app.mk
diff --git a/app/test-pmd/config.json b/app/test-pmd/config.json
new file mode 100644
index 0000000..4589dbc
--- /dev/null
+++ b/app/test-pmd/config.json
@@ -0,0 +1,33 @@
+{
+	"DPDK":
+	{
+		"v": "",
+		"l": "0-4",
+		"n": "4",
+		"master-lcore": "0",
+		"proc-type": "primary"
+	},
+	"TEST-PMD":
+	{
+		"i": "",
+		"portmask": "0xff",
+		"nb-cores": "4",
+		"port-topology": "paired"
+	},
+	"DPDK.vdev0":
+	{
+		"net_ring0": ""
+	},
+	"DPDK.vdev1":
+	{
+		"net_ring1": ""
+	},
+	"DPDK.vdev2":
+	{
+		"net_ring2": ""
+	},
+	"DPDK.vdev3":
+	{
+		"net_ring3": ""
+	}
+}
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index 7b82976..014bb46 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -46,6 +46,10 @@
 
 #include <stdint.h>
 #include <unistd.h>
+
+#ifdef RTE_JSON_SUPPORT
+#include <jansson.h>
+#endif
 #include <inttypes.h>
 
 #include <rte_common.h>
@@ -2290,6 +2294,87 @@ cfgfile_load_path(int argc, char **argv)
 	}
 	return NULL;
 }
+
+#ifdef RTE_JSON_SUPPORT
+/*
+ * Decoding JSON structure to rte_cfgfile structure.
+ * Returns handler to cfgfile object, NULL if error.
+ */
+static struct
+rte_cfgfile *l3fwd_json_to_cfg(json_t *json, int flags)
+{
+	if (!json) {
+		printf("Error: JSON structure is NULL, nothing to parse\n");
+		return NULL;
+	}
+	/* create an empty instance of cfgfile structure */
+	struct rte_cfgfile *cfgfile = rte_cfgfile_create(flags);
+
+	if (!cfgfile)
+		return NULL;
+
+	const char *section;
+	json_t *entry;
+
+	/* set pointer to first section */
+	void *iter_section = json_object_iter(json);
+
+	while (iter_section) {
+
+		section = json_object_iter_key(iter_section);
+		entry = json_object_iter_value(iter_section);
+
+		/* add parsed section name of current section to cfgfile */
+		rte_cfgfile_add_section(cfgfile, section);
+
+		/* set pointer to first entry */
+		void *iter_entry = json_object_iter(entry);
+
+		while (iter_entry) {
+
+			const char *key;
+			const char *value;
+
+			key = json_object_iter_key(iter_entry);
+			value = json_string_value(
+					json_object_iter_value(iter_entry));
+
+			/* add parsed key and value of current entry */
+			/* to cfgfile */
+			rte_cfgfile_add_entry(cfgfile, section, key, value);
+
+			/* pointer to next entry */
+			iter_entry = json_object_iter_next(entry, iter_entry);
+		}
+		/* pointer to next section */
+		iter_section = json_object_iter_next(json, iter_section);
+	}
+	return cfgfile;
+}
+
+/*
+ * Check presence and load JSON file to rte_cfgfile structure.
+ * Returns handler to cfgfile object, NULL if error.
+ */
+static struct
+rte_cfgfile *l3fwd_load_json_to_cfg(const char *path)
+{
+	struct rte_cfgfile *cfgfile = NULL;
+	/* check if config file exist */
+	if (access(path, F_OK) != -1) {
+		/* parse JSON file */
+		json_error_t error;
+		json_t *json = json_load_file(path, 0, &error);
+
+		if (json)
+			cfgfile = l3fwd_json_to_cfg(json, 0);
+		else
+			fprintf(stderr, "JSON error on line %d: %s\n",
+							error.line, error.text);
+	}
+	return cfgfile;
+}
+#endif
 #endif
 
 #define APP_NAME "TEST-PMD"
@@ -2318,9 +2403,16 @@ main(int argc, char** argv)
 				"in default directory)\n");
 		config_file = strdup("config.ini");
 	}
-
+#ifndef RTE_JSON_SUPPORT
 	cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+#endif
 
+#ifdef RTE_JSON_SUPPORT
+	if (strstr(config_file, ".ini"))
+		cfg = rte_cfgfile_load(config_file, CFG_FLAG_EMPTY_VALUES);
+	else if (strstr(config_file, ".json"))
+		cfg = l3fwd_load_json_to_cfg(config_file);
+#endif
 	if (cfg == NULL) {
 		printf("Info: Valid cfgfile not found\n");
 	} else {
diff --git a/config/common_base b/config/common_base
index 8ae6e92..98e8fa2 100644
--- a/config/common_base
+++ b/config/common_base
@@ -744,3 +744,8 @@ CONFIG_RTE_APP_CRYPTO_PERF=y
 # Compile the eventdev application
 #
 CONFIG_RTE_APP_EVENTDEV=y
+
+#
+# Compile JSON support
+#
+CONFIG_RTE_JSON_SUPPORT=n
\ No newline at end of file
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 70+ messages in thread

* Re: [PATCH v4 0/5] Rework cfgfile API to enable apps config file support
  2017-07-10 15:13             ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Thomas Monjalon
@ 2017-07-20 21:48               ` Thomas Monjalon
  0 siblings, 0 replies; 70+ messages in thread
From: Thomas Monjalon @ 2017-07-20 21:48 UTC (permalink / raw)
  To: Jacek Piasecki
  Cc: dev, Cristian Dumitrescu, bruce.richardson, deepak.k.jain,
	kubax.kozak, michalx.k.jastrzebski

10/07/2017 18:13, Thomas Monjalon:
> The maintainer of librte_cfgfile is Cristian (+Cc),
> please set him as recipient for next time.
> 
> 10/07/2017 14:44, Jacek Piasecki:
> > New API for cfgfile library allows to create a cfgfile at runtime, add new
> > section, add entry in a section, update existing entry and save cfgfile
> > structure to INI file - opens up the possibility to have applications
> > dynamically build up a proper DPDK configuration, rather than
> > having to have a pre-existing one. Due the new API functions, simplification
> > of load() function was made. One new unit test to TEST app was added. It
> > contains an example of a large INI file whose parsing requires multiple
> > reallocation of memory.

Still no review.
It is postponed to 17.11.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v4 1/5] cfgfile: remove EAL dependency
  2017-07-10 12:44             ` [PATCH v4 1/5] cfgfile: remove EAL dependency Jacek Piasecki
@ 2017-08-30 17:58               ` Bruce Richardson
  0 siblings, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-08-30 17:58 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain, kubax.kozak, michalx.k.jastrzebski

On Mon, Jul 10, 2017 at 02:44:13PM +0200, Jacek Piasecki wrote:
> This patch removes the dependency to EAL in cfgfile library.
> 
> Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>

Looks ok to me

Acked-by: Bruce Richardson <bruce.richardson@intel.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v4 2/5] cfgfile: change existing API functions
  2017-07-10 12:44             ` [PATCH v4 2/5] cfgfile: change existing API functions Jacek Piasecki
@ 2017-08-30 20:07               ` Bruce Richardson
  0 siblings, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-08-30 20:07 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain, kubax.kozak, michalx.k.jastrzebski

I think the commit title needs rewording. This changes the internals not
the API.

On Mon, Jul 10, 2017 at 02:44:14PM +0200, Jacek Piasecki wrote:
> Change to flat arrays in cfgfile struct force slightly
> diffrent data access for most of cfgfile functions.
> This patch provides necessary changes in existing API.
> 
> Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
> ---

Some comments below. I believe the change in return value from -1 to
-EINVAL, though a more correct error, actually counts as an ABI change,
so I think that should be removed, i.e. keep errors at -1. Once done:

Acked-by: Bruce Richardon <bruce.richardson@intel.com>

>  lib/librte_cfgfile/rte_cfgfile.c | 120 +++++++++++++++++++--------------------
>  lib/librte_cfgfile/rte_cfgfile.h |   6 +-
>  2 files changed, 62 insertions(+), 64 deletions(-)
> 
> diff --git a/lib/librte_cfgfile/rte_cfgfile.c b/lib/librte_cfgfile/rte_cfgfile.c
> index c6ae3e3..50fe37a 100644
> --- a/lib/librte_cfgfile/rte_cfgfile.c
> +++ b/lib/librte_cfgfile/rte_cfgfile.c
> @@ -35,6 +35,7 @@
>  #include <stdlib.h>
>  #include <string.h>
>  #include <ctype.h>
> +#include <errno.h>
>  #include <rte_common.h>
>  
>  #include "rte_cfgfile.h"
> @@ -42,13 +43,15 @@
>  struct rte_cfgfile_section {
>  	char name[CFG_NAME_LEN];
>  	int num_entries;
> -	struct rte_cfgfile_entry *entries[0];
> +	int allocated_entries;
> +	struct rte_cfgfile_entry *entries;
>  };
>  
>  struct rte_cfgfile {
>  	int flags;
>  	int num_sections;
> -	struct rte_cfgfile_section *sections[0];
> +	int allocated_sections;
> +	struct rte_cfgfile_section *sections;
>  };
> 

These are good changes, allowing us to have the sections array and
entries arrays separate from the basic data structures.

<snip>
> @@ -409,7 +407,7 @@ rte_cfgfile_section_num_entries(struct rte_cfgfile *cfg,
>  {
>  	const struct rte_cfgfile_section *s = _get_section(cfg, sectionname);
>  	if (s == NULL)
> -		return -1;
> +		return -EINVAL;
>  	return s->num_entries;
>  }

I think this change should be dropped for backward compatibility, or
else put in NEXT_ABI #ifdefs and an ABI notice added to the RN.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v4 3/5] cfgfile: add new functions to API
  2017-07-10 12:44             ` [PATCH v4 3/5] cfgfile: add new functions to API Jacek Piasecki
@ 2017-08-30 20:18               ` Bruce Richardson
  0 siblings, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-08-30 20:18 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain, kubax.kozak, michalx.k.jastrzebski

Title needs a reword to indicate the types of new API functions e.g.:
cfgfile: add APIs for cfgfile modification

On Mon, Jul 10, 2017 at 02:44:15PM +0200, Jacek Piasecki wrote:
> Extend existing cfgfile library with providing new API functions:
> 
> rte_cfgfile_create() - create new cfgfile object
> rte_cfgfile_add_section() - add new section to existing cfgfile
> object
> rte_cfgfile_add_entry() - add new entry to existing cfgfile
> object in specified section
> rte_cfgfile_set_entry() - update existing entry in cfgfile object
> rte_cfgfile_save() - save existing cfgfile object to INI file
> 
> This modification allows to create a cfgfile on
> runtime and opens up the possibility to have applications
> dynamically build up a proper DPDK configuration, rather than having
> to have a pre-existing one.
> 
> Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
> ---

Good additions

Acked-by: Bruce Richardson <bruce.richardson@intel.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v4 4/5] cfgfile: rework of load function
  2017-07-10 12:44             ` [PATCH v4 4/5] cfgfile: rework of load function Jacek Piasecki
@ 2017-08-30 20:24               ` Bruce Richardson
  0 siblings, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-08-30 20:24 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain, kubax.kozak, michalx.k.jastrzebski

On Mon, Jul 10, 2017 at 02:44:16PM +0200, Jacek Piasecki wrote:
> New functions added to cfgfile library make it possible
> to significantly simplify the code of rte_cfgfile_load_with_params()
> 
> This patch shows the new body of this function.
> 
> Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
> ---
>  lib/librte_cfgfile/rte_cfgfile.c | 156 ++++++++-------------------------------
>  1 file changed, 29 insertions(+), 127 deletions(-)
> 
Acked-by: Bruce Richardson <bruce.richardson@intel.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v4 5/5] test/cfgfile: add new unit test
  2017-07-10 12:44             ` [PATCH v4 5/5] test/cfgfile: add new unit test Jacek Piasecki
@ 2017-08-30 20:25               ` Bruce Richardson
  2017-09-04  9:21                 ` Bruce Richardson
  2017-09-04  9:30               ` Bruce Richardson
  1 sibling, 1 reply; 70+ messages in thread
From: Bruce Richardson @ 2017-08-30 20:25 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain, kubax.kozak, michalx.k.jastrzebski

On Mon, Jul 10, 2017 at 02:44:17PM +0200, Jacek Piasecki wrote:
> Load huge realloc_sections.ini file to check malloc/realloc
> ability of cfgfile library.
> 
> Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
> ---

This may need some work still. The cfgfile tests fail for me when I run
them:

RTE>>cfgfile_autotest
TestCase test_cfgfile_realloc_sections() line 165 failed: Failed to load config file
Test Failed
RTE>>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v4 5/5] test/cfgfile: add new unit test
  2017-08-30 20:25               ` Bruce Richardson
@ 2017-09-04  9:21                 ` Bruce Richardson
  0 siblings, 0 replies; 70+ messages in thread
From: Bruce Richardson @ 2017-09-04  9:21 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain, kubax.kozak, michalx.k.jastrzebski

On Wed, Aug 30, 2017 at 09:25:51PM +0100, Bruce Richardson wrote:
> On Mon, Jul 10, 2017 at 02:44:17PM +0200, Jacek Piasecki wrote:
> > Load huge realloc_sections.ini file to check malloc/realloc
> > ability of cfgfile library.
> > 
> > Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
> > ---
> 
> This may need some work still. The cfgfile tests fail for me when I run
> them:
> 
> RTE>>cfgfile_autotest
> TestCase test_cfgfile_realloc_sections() line 165 failed: Failed to load config file
> Test Failed
> RTE>>
> 
Deleting the build directory and recreating it allows these tests to
pass. Following up with Jacek offline, it appears that the test data
used here is not copied over each time on build, which is what causes
the error - using old data/resources with the new tests. Therefore, it
appears the tests themselves are not at fault.

Acked-by: Bruce Richardson <bruce.richardson@intel.com>

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v4 5/5] test/cfgfile: add new unit test
  2017-07-10 12:44             ` [PATCH v4 5/5] test/cfgfile: add new unit test Jacek Piasecki
  2017-08-30 20:25               ` Bruce Richardson
@ 2017-09-04  9:30               ` Bruce Richardson
  2017-09-15 13:56                 ` Thomas Monjalon
  1 sibling, 1 reply; 70+ messages in thread
From: Bruce Richardson @ 2017-09-04  9:30 UTC (permalink / raw)
  To: Jacek Piasecki; +Cc: dev, deepak.k.jain, kubax.kozak, michalx.k.jastrzebski

On Mon, Jul 10, 2017 at 02:44:17PM +0200, Jacek Piasecki wrote:
> Load huge realloc_sections.ini file to check malloc/realloc
> ability of cfgfile library.
> 
> Signed-off-by: Jacek Piasecki <jacekx.piasecki@intel.com>
> ---
>  test/test/test_cfgfile.c                         |  40 +++++++
>  test/test/test_cfgfiles/etc/realloc_sections.ini | 128 +++++++++++++++++++++++
>  2 files changed, 168 insertions(+)
>  create mode 100644 test/test/test_cfgfiles/etc/realloc_sections.ini
> 
> diff --git a/test/test/test_cfgfile.c b/test/test/test_cfgfile.c
> index 4cc9b14..2278618 100644
> --- a/test/test/test_cfgfile.c
> +++ b/test/test/test_cfgfile.c
> @@ -111,6 +111,7 @@ _test_cfgfile_sample(struct rte_cfgfile *cfgfile)
>  	return 0;
>  }
>  
> +
>  static int
>  test_cfgfile_sample1(void)
>  {
> @@ -154,6 +155,42 @@ test_cfgfile_sample2(void)
>  }
>  
>  static int
> +test_cfgfile_realloc_sections(void)
> +{
> +	struct rte_cfgfile *cfgfile;
> +	int ret;
> +	const char *value;
> +
> +	cfgfile = rte_cfgfile_load(CFG_FILES_ETC "/realloc_sections.ini", 0);
> +	TEST_ASSERT_NOT_NULL(cfgfile, "Failed to load config file");
> +
> +	ret = rte_cfgfile_num_sections(cfgfile, NULL, 0);
> +	TEST_ASSERT(ret == 9, "Unexpected number of sections: %d", ret);
> +
> +	ret = rte_cfgfile_has_section(cfgfile, "section9");
> +	TEST_ASSERT(ret, "section9 missing");
> +
> +	ret = rte_cfgfile_section_num_entries(cfgfile, "section3");
> +	TEST_ASSERT(ret == 21,
> +			"section3 unexpected number of entries: %d", ret);
> +
> +	ret = rte_cfgfile_section_num_entries(cfgfile, "section9");
> +	TEST_ASSERT(ret == 8, "section9 unexpected number of entries: %d", ret);
> +
> +	value = rte_cfgfile_get_entry(cfgfile, "section9", "key8");
> +	TEST_ASSERT(strcmp("value8_section9", value) == 0,
> +		    "key unexpected value: %s", value);
> +
> +	ret = rte_cfgfile_save(cfgfile, "cfgfile_save.ini");
> +	TEST_ASSERT_SUCCESS(ret, "Failed to save *.ini file");

This file is a temporary one, so:
a) should be placed in /tmp
b) should be removed at the end of the test.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v4 5/5] test/cfgfile: add new unit test
  2017-09-04  9:30               ` Bruce Richardson
@ 2017-09-15 13:56                 ` Thomas Monjalon
  2017-09-18 13:49                   ` Jastrzebski, MichalX K
  0 siblings, 1 reply; 70+ messages in thread
From: Thomas Monjalon @ 2017-09-15 13:56 UTC (permalink / raw)
  To: Jacek Piasecki
  Cc: dev, Bruce Richardson, deepak.k.jain, kubax.kozak, michalx.k.jastrzebski

04/09/2017 11:30, Bruce Richardson:
> This file is a temporary one, so:
> a) should be placed in /tmp
> b) should be removed at the end of the test.

Jacek, do you plan to update this series in 17.11 timeframe?

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v4 5/5] test/cfgfile: add new unit test
  2017-09-15 13:56                 ` Thomas Monjalon
@ 2017-09-18 13:49                   ` Jastrzebski, MichalX K
  0 siblings, 0 replies; 70+ messages in thread
From: Jastrzebski, MichalX K @ 2017-09-18 13:49 UTC (permalink / raw)
  To: Thomas Monjalon, Piasecki, JacekX
  Cc: dev, Richardson, Bruce, Jain, Deepak K, Kozak, KubaX

> -----Original Message-----
> From: Thomas Monjalon [mailto:thomas@monjalon.net]
> Sent: Friday, September 15, 2017 3:56 PM
> To: Piasecki, JacekX <jacekx.piasecki@intel.com>
> Cc: dev@dpdk.org; Richardson, Bruce <bruce.richardson@intel.com>; Jain,
> Deepak K <deepak.k.jain@intel.com>; Kozak, KubaX
> <kubax.kozak@intel.com>; Jastrzebski, MichalX K
> <michalx.k.jastrzebski@intel.com>
> Subject: Re: [dpdk-dev] [PATCH v4 5/5] test/cfgfile: add new unit test
> 
> 04/09/2017 11:30, Bruce Richardson:
> > This file is a temporary one, so:
> > a) should be placed in /tmp
> > b) should be removed at the end of the test.
> 
> Jacek, do you plan to update this series in 17.11 timeframe?

Hi Thomas, yes we plan to update the series.
V5 should be sent tomorrow.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5 0/3] EAL change for using a config file for DPDK
  2017-07-13 10:07               ` Kuba Kozak
                                   ` (2 preceding siblings ...)
  2017-07-13 10:07                 ` [PATCH v5 3/3] app/testpmd: add parse options from JSON " Kuba Kozak
@ 2019-01-23 19:31                 ` Ferruh Yigit
  2019-01-23 20:26                   ` Thomas Monjalon
  3 siblings, 1 reply; 70+ messages in thread
From: Ferruh Yigit @ 2019-01-23 19:31 UTC (permalink / raw)
  To: Kuba Kozak
  Cc: deepak.k.jain, bruce.richardson, michalx.k.jastrzebski,
	jacekx.piasecki, dpdk-dev, Thomas Monjalon, Stephen Hemminger,
	Kevin Traynor, David Marchand

On 7/13/2017 11:07 AM, kubax.kozak at intel.com (Kuba Kozak) wrote:
> This patchset introduce a mechanism for running dpdk application with 
> parameters provided by configuration file.
> 
> A new API for EAL takes a config file data type - either loaded from
> file, or built up programmatically in the application - and extracts
> DPDK parameters from it to be used when eal init is called. 
> This allows apps to have an alternative method to configure EAL,
> other than via command-line parameters.
> 
> Reworked applications are used to demonstrate the new eal API.
> If a --cfgfile-path <path> option is passed into command line non
> EAL section, then the file is loaded and used by app. If a file
> called config.ini is present in current working directory, and
> no --cfgfile-path option is passed in, config.ini file will be
> loaded and used by app.
> 
> Patch "app/testpmd: add parse options from JSON cfg file" 
> demonstrates the usage of JSON instead of INI file format. 
> JSON file can be called the same way as above, 
> through --cfgfile-path <path> argument.
> ---
> this patch depends on:
> "Rework cfgfile API to enable apps config file support"
> 
> v5:
>   changed define "RTE_DEVTYPE_VIRTUAL" to "RTE_DEVTYPE_UNDEFINED"
>   due to compilation errors (changes on current master).
> 
> v4:
>  Code optimalisation in parse_vdev_devices() function.
>  Moved some functions from librte_eal/bsdapp and librte_eal/linuxapp
>  to the librte_eal/common.
>  Bug fixes.
> 
> v3: 
>  split one patchset into two distinct patchsets:
>  1. cfgfile library and TEST app changes
>  2. EAL changes and examples (this patchset depends on cfgfile)
> 
> v2:
>   lib eal:
> 	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
> 	Now this function load data from cfg structure and did initial
> 	initialization of EAL arguments. Vdev argument are stored in different
> 	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
> 	function it is necessary to call rte_eal_init to complete EAL
> 	initialization. There is no more merging arguments from different
> 	sources (cfg file and command line).
>   	Added non_eal_configure to testpmd application.
> 	Function maintain the same functionality as rte_eal_configure but
> 	for non-eal arguments. 
>   	Added config JSON feature to testpmd last patch from patchset contain
> 	example showing use of .json configuration files.
> 
>   lib cfgfile:
>   	Rework of add_section(), add_entry() new implementation
>   	New members allocated_entries/sections, free_entries/sections
> 	in rte_cfgfile structure, change in array of pointers
> 	**sections, **entries instead of *sections[], *entries[]
>   	Add  set_entry() to update/overwrite already existing entry in cfgfile
> 	struct
>   	Add save() function to save on disc cfgfile structure in INI format
>   	Rework of existing load() function  simplifying the code
>   	Add unit test realloc_sections() in TEST app for testing realloc/malloc
> 	of new API functions, add test for save() function
> 
> Kuba Kozak (3):
>   eal: add functions parsing EAL arguments
>   app/testpmd: add parse options from cfg file
>   app/testpmd: add parse options from JSON cfg file

This patchset is idle more than a year now.
It solves problem of eal parameters, it doesn't remove them but at least moves
from command line to config file.

The patch seems mostly done, but what is the status of it, do we want to
continue it?
And if we want to continue it can this be a good candidate for GCOS?

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5 0/3] EAL change for using a config file for DPDK
  2019-01-23 19:31                 ` [PATCH v5 0/3] EAL change for using a config file for DPDK Ferruh Yigit
@ 2019-01-23 20:26                   ` Thomas Monjalon
  2019-01-24 13:54                     ` Ferruh Yigit
  0 siblings, 1 reply; 70+ messages in thread
From: Thomas Monjalon @ 2019-01-23 20:26 UTC (permalink / raw)
  To: Ferruh Yigit
  Cc: Kuba Kozak, deepak.k.jain, bruce.richardson,
	michalx.k.jastrzebski, jacekx.piasecki, dpdk-dev,
	Stephen Hemminger, Kevin Traynor, David Marchand

23/01/2019 20:31, Ferruh Yigit:
> On 7/13/2017 11:07 AM, kubax.kozak at intel.com (Kuba Kozak) wrote:
> > This patchset introduce a mechanism for running dpdk application with 
> > parameters provided by configuration file.
> > 
> > A new API for EAL takes a config file data type - either loaded from
> > file, or built up programmatically in the application - and extracts
> > DPDK parameters from it to be used when eal init is called. 
> > This allows apps to have an alternative method to configure EAL,
> > other than via command-line parameters.
> > 
> > Reworked applications are used to demonstrate the new eal API.
> > If a --cfgfile-path <path> option is passed into command line non
> > EAL section, then the file is loaded and used by app. If a file
> > called config.ini is present in current working directory, and
> > no --cfgfile-path option is passed in, config.ini file will be
> > loaded and used by app.
> > 
> > Patch "app/testpmd: add parse options from JSON cfg file" 
> > demonstrates the usage of JSON instead of INI file format. 
> > JSON file can be called the same way as above, 
> > through --cfgfile-path <path> argument.
> > ---
> > this patch depends on:
> > "Rework cfgfile API to enable apps config file support"
> > 
> > v5:
> >   changed define "RTE_DEVTYPE_VIRTUAL" to "RTE_DEVTYPE_UNDEFINED"
> >   due to compilation errors (changes on current master).
> > 
> > v4:
> >  Code optimalisation in parse_vdev_devices() function.
> >  Moved some functions from librte_eal/bsdapp and librte_eal/linuxapp
> >  to the librte_eal/common.
> >  Bug fixes.
> > 
> > v3: 
> >  split one patchset into two distinct patchsets:
> >  1. cfgfile library and TEST app changes
> >  2. EAL changes and examples (this patchset depends on cfgfile)
> > 
> > v2:
> >   lib eal:
> > 	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
> > 	Now this function load data from cfg structure and did initial
> > 	initialization of EAL arguments. Vdev argument are stored in different
> > 	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
> > 	function it is necessary to call rte_eal_init to complete EAL
> > 	initialization. There is no more merging arguments from different
> > 	sources (cfg file and command line).
> >   	Added non_eal_configure to testpmd application.
> > 	Function maintain the same functionality as rte_eal_configure but
> > 	for non-eal arguments. 
> >   	Added config JSON feature to testpmd last patch from patchset contain
> > 	example showing use of .json configuration files.
> > 
> >   lib cfgfile:
> >   	Rework of add_section(), add_entry() new implementation
> >   	New members allocated_entries/sections, free_entries/sections
> > 	in rte_cfgfile structure, change in array of pointers
> > 	**sections, **entries instead of *sections[], *entries[]
> >   	Add  set_entry() to update/overwrite already existing entry in cfgfile
> > 	struct
> >   	Add save() function to save on disc cfgfile structure in INI format
> >   	Rework of existing load() function  simplifying the code
> >   	Add unit test realloc_sections() in TEST app for testing realloc/malloc
> > 	of new API functions, add test for save() function
> > 
> > Kuba Kozak (3):
> >   eal: add functions parsing EAL arguments
> >   app/testpmd: add parse options from cfg file
> >   app/testpmd: add parse options from JSON cfg file
> 
> This patchset is idle more than a year now.
> It solves problem of eal parameters, it doesn't remove them but at least moves
> from command line to config file.
> 
> The patch seems mostly done, but what is the status of it, do we want to
> continue it?
> And if we want to continue it can this be a good candidate for GCOS?

I think we must focus on reorganization of EAL first.
When the options parsing will be better isolated,
and accessible from API independant of rte_eal_init,
then we could provide some helpers to use those APIs
for a config file, a custom command line or anything else.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5 0/3] EAL change for using a config file for DPDK
  2019-01-23 20:26                   ` Thomas Monjalon
@ 2019-01-24 13:54                     ` Ferruh Yigit
  2019-01-24 14:32                       ` Thomas Monjalon
  0 siblings, 1 reply; 70+ messages in thread
From: Ferruh Yigit @ 2019-01-24 13:54 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Kuba Kozak, deepak.k.jain, bruce.richardson,
	michalx.k.jastrzebski, jacekx.piasecki, dpdk-dev,
	Stephen Hemminger, Kevin Traynor, David Marchand

On 1/23/2019 8:26 PM, Thomas Monjalon wrote:
> 23/01/2019 20:31, Ferruh Yigit:
>> On 7/13/2017 11:07 AM, kubax.kozak at intel.com (Kuba Kozak) wrote:
>>> This patchset introduce a mechanism for running dpdk application with 
>>> parameters provided by configuration file.
>>>
>>> A new API for EAL takes a config file data type - either loaded from
>>> file, or built up programmatically in the application - and extracts
>>> DPDK parameters from it to be used when eal init is called. 
>>> This allows apps to have an alternative method to configure EAL,
>>> other than via command-line parameters.
>>>
>>> Reworked applications are used to demonstrate the new eal API.
>>> If a --cfgfile-path <path> option is passed into command line non
>>> EAL section, then the file is loaded and used by app. If a file
>>> called config.ini is present in current working directory, and
>>> no --cfgfile-path option is passed in, config.ini file will be
>>> loaded and used by app.
>>>
>>> Patch "app/testpmd: add parse options from JSON cfg file" 
>>> demonstrates the usage of JSON instead of INI file format. 
>>> JSON file can be called the same way as above, 
>>> through --cfgfile-path <path> argument.
>>> ---
>>> this patch depends on:
>>> "Rework cfgfile API to enable apps config file support"
>>>
>>> v5:
>>>   changed define "RTE_DEVTYPE_VIRTUAL" to "RTE_DEVTYPE_UNDEFINED"
>>>   due to compilation errors (changes on current master).
>>>
>>> v4:
>>>  Code optimalisation in parse_vdev_devices() function.
>>>  Moved some functions from librte_eal/bsdapp and librte_eal/linuxapp
>>>  to the librte_eal/common.
>>>  Bug fixes.
>>>
>>> v3: 
>>>  split one patchset into two distinct patchsets:
>>>  1. cfgfile library and TEST app changes
>>>  2. EAL changes and examples (this patchset depends on cfgfile)
>>>
>>> v2:
>>>   lib eal:
>>> 	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
>>> 	Now this function load data from cfg structure and did initial
>>> 	initialization of EAL arguments. Vdev argument are stored in different
>>> 	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
>>> 	function it is necessary to call rte_eal_init to complete EAL
>>> 	initialization. There is no more merging arguments from different
>>> 	sources (cfg file and command line).
>>>   	Added non_eal_configure to testpmd application.
>>> 	Function maintain the same functionality as rte_eal_configure but
>>> 	for non-eal arguments. 
>>>   	Added config JSON feature to testpmd last patch from patchset contain
>>> 	example showing use of .json configuration files.
>>>
>>>   lib cfgfile:
>>>   	Rework of add_section(), add_entry() new implementation
>>>   	New members allocated_entries/sections, free_entries/sections
>>> 	in rte_cfgfile structure, change in array of pointers
>>> 	**sections, **entries instead of *sections[], *entries[]
>>>   	Add  set_entry() to update/overwrite already existing entry in cfgfile
>>> 	struct
>>>   	Add save() function to save on disc cfgfile structure in INI format
>>>   	Rework of existing load() function  simplifying the code
>>>   	Add unit test realloc_sections() in TEST app for testing realloc/malloc
>>> 	of new API functions, add test for save() function
>>>
>>> Kuba Kozak (3):
>>>   eal: add functions parsing EAL arguments
>>>   app/testpmd: add parse options from cfg file
>>>   app/testpmd: add parse options from JSON cfg file
>>
>> This patchset is idle more than a year now.
>> It solves problem of eal parameters, it doesn't remove them but at least moves
>> from command line to config file.
>>
>> The patch seems mostly done, but what is the status of it, do we want to
>> continue it?
>> And if we want to continue it can this be a good candidate for GCOS?
> 
> I think we must focus on reorganization of EAL first.
> When the options parsing will be better isolated,
> and accessible from API independant of rte_eal_init,
> then we could provide some helpers to use those APIs
> for a config file, a custom command line or anything else.

Is there any actions do we need to take when patches are rejected?

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5 0/3] EAL change for using a config file for DPDK
  2019-01-24 13:54                     ` Ferruh Yigit
@ 2019-01-24 14:32                       ` Thomas Monjalon
  2019-01-24 14:46                         ` Ferruh Yigit
  0 siblings, 1 reply; 70+ messages in thread
From: Thomas Monjalon @ 2019-01-24 14:32 UTC (permalink / raw)
  To: Ferruh Yigit
  Cc: Kuba Kozak, deepak.k.jain, bruce.richardson,
	michalx.k.jastrzebski, jacekx.piasecki, dpdk-dev,
	Stephen Hemminger, Kevin Traynor, David Marchand

24/01/2019 14:54, Ferruh Yigit:
> On 1/23/2019 8:26 PM, Thomas Monjalon wrote:
> > 23/01/2019 20:31, Ferruh Yigit:
> >> On 7/13/2017 11:07 AM, kubax.kozak at intel.com (Kuba Kozak) wrote:
> >>> This patchset introduce a mechanism for running dpdk application with 
> >>> parameters provided by configuration file.
> >>>
> >>> A new API for EAL takes a config file data type - either loaded from
> >>> file, or built up programmatically in the application - and extracts
> >>> DPDK parameters from it to be used when eal init is called. 
> >>> This allows apps to have an alternative method to configure EAL,
> >>> other than via command-line parameters.
> >>>
> >>> Reworked applications are used to demonstrate the new eal API.
> >>> If a --cfgfile-path <path> option is passed into command line non
> >>> EAL section, then the file is loaded and used by app. If a file
> >>> called config.ini is present in current working directory, and
> >>> no --cfgfile-path option is passed in, config.ini file will be
> >>> loaded and used by app.
> >>>
> >>> Patch "app/testpmd: add parse options from JSON cfg file" 
> >>> demonstrates the usage of JSON instead of INI file format. 
> >>> JSON file can be called the same way as above, 
> >>> through --cfgfile-path <path> argument.
> >>> ---
> >>> this patch depends on:
> >>> "Rework cfgfile API to enable apps config file support"
> >>>
> >>> v5:
> >>>   changed define "RTE_DEVTYPE_VIRTUAL" to "RTE_DEVTYPE_UNDEFINED"
> >>>   due to compilation errors (changes on current master).
> >>>
> >>> v4:
> >>>  Code optimalisation in parse_vdev_devices() function.
> >>>  Moved some functions from librte_eal/bsdapp and librte_eal/linuxapp
> >>>  to the librte_eal/common.
> >>>  Bug fixes.
> >>>
> >>> v3: 
> >>>  split one patchset into two distinct patchsets:
> >>>  1. cfgfile library and TEST app changes
> >>>  2. EAL changes and examples (this patchset depends on cfgfile)
> >>>
> >>> v2:
> >>>   lib eal:
> >>> 	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
> >>> 	Now this function load data from cfg structure and did initial
> >>> 	initialization of EAL arguments. Vdev argument are stored in different
> >>> 	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
> >>> 	function it is necessary to call rte_eal_init to complete EAL
> >>> 	initialization. There is no more merging arguments from different
> >>> 	sources (cfg file and command line).
> >>>   	Added non_eal_configure to testpmd application.
> >>> 	Function maintain the same functionality as rte_eal_configure but
> >>> 	for non-eal arguments. 
> >>>   	Added config JSON feature to testpmd last patch from patchset contain
> >>> 	example showing use of .json configuration files.
> >>>
> >>>   lib cfgfile:
> >>>   	Rework of add_section(), add_entry() new implementation
> >>>   	New members allocated_entries/sections, free_entries/sections
> >>> 	in rte_cfgfile structure, change in array of pointers
> >>> 	**sections, **entries instead of *sections[], *entries[]
> >>>   	Add  set_entry() to update/overwrite already existing entry in cfgfile
> >>> 	struct
> >>>   	Add save() function to save on disc cfgfile structure in INI format
> >>>   	Rework of existing load() function  simplifying the code
> >>>   	Add unit test realloc_sections() in TEST app for testing realloc/malloc
> >>> 	of new API functions, add test for save() function
> >>>
> >>> Kuba Kozak (3):
> >>>   eal: add functions parsing EAL arguments
> >>>   app/testpmd: add parse options from cfg file
> >>>   app/testpmd: add parse options from JSON cfg file
> >>
> >> This patchset is idle more than a year now.
> >> It solves problem of eal parameters, it doesn't remove them but at least moves
> >> from command line to config file.
> >>
> >> The patch seems mostly done, but what is the status of it, do we want to
> >> continue it?
> >> And if we want to continue it can this be a good candidate for GCOS?
> > 
> > I think we must focus on reorganization of EAL first.
> > When the options parsing will be better isolated,
> > and accessible from API independant of rte_eal_init,
> > then we could provide some helpers to use those APIs
> > for a config file, a custom command line or anything else.
> 
> Is there any actions do we need to take when patches are rejected?

Not sure I understand your question.
Any opinion about such plan?

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5 0/3] EAL change for using a config file for DPDK
  2019-01-24 14:32                       ` Thomas Monjalon
@ 2019-01-24 14:46                         ` Ferruh Yigit
  2019-01-24 16:06                           ` Thomas Monjalon
  0 siblings, 1 reply; 70+ messages in thread
From: Ferruh Yigit @ 2019-01-24 14:46 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Kuba Kozak, deepak.k.jain, bruce.richardson,
	michalx.k.jastrzebski, jacekx.piasecki, dpdk-dev,
	Stephen Hemminger, Kevin Traynor, David Marchand

On 1/24/2019 2:32 PM, Thomas Monjalon wrote:
> 24/01/2019 14:54, Ferruh Yigit:
>> On 1/23/2019 8:26 PM, Thomas Monjalon wrote:
>>> 23/01/2019 20:31, Ferruh Yigit:
>>>> On 7/13/2017 11:07 AM, kubax.kozak at intel.com (Kuba Kozak) wrote:
>>>>> This patchset introduce a mechanism for running dpdk application with 
>>>>> parameters provided by configuration file.
>>>>>
>>>>> A new API for EAL takes a config file data type - either loaded from
>>>>> file, or built up programmatically in the application - and extracts
>>>>> DPDK parameters from it to be used when eal init is called. 
>>>>> This allows apps to have an alternative method to configure EAL,
>>>>> other than via command-line parameters.
>>>>>
>>>>> Reworked applications are used to demonstrate the new eal API.
>>>>> If a --cfgfile-path <path> option is passed into command line non
>>>>> EAL section, then the file is loaded and used by app. If a file
>>>>> called config.ini is present in current working directory, and
>>>>> no --cfgfile-path option is passed in, config.ini file will be
>>>>> loaded and used by app.
>>>>>
>>>>> Patch "app/testpmd: add parse options from JSON cfg file" 
>>>>> demonstrates the usage of JSON instead of INI file format. 
>>>>> JSON file can be called the same way as above, 
>>>>> through --cfgfile-path <path> argument.
>>>>> ---
>>>>> this patch depends on:
>>>>> "Rework cfgfile API to enable apps config file support"
>>>>>
>>>>> v5:
>>>>>   changed define "RTE_DEVTYPE_VIRTUAL" to "RTE_DEVTYPE_UNDEFINED"
>>>>>   due to compilation errors (changes on current master).
>>>>>
>>>>> v4:
>>>>>  Code optimalisation in parse_vdev_devices() function.
>>>>>  Moved some functions from librte_eal/bsdapp and librte_eal/linuxapp
>>>>>  to the librte_eal/common.
>>>>>  Bug fixes.
>>>>>
>>>>> v3: 
>>>>>  split one patchset into two distinct patchsets:
>>>>>  1. cfgfile library and TEST app changes
>>>>>  2. EAL changes and examples (this patchset depends on cfgfile)
>>>>>
>>>>> v2:
>>>>>   lib eal:
>>>>> 	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
>>>>> 	Now this function load data from cfg structure and did initial
>>>>> 	initialization of EAL arguments. Vdev argument are stored in different
>>>>> 	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
>>>>> 	function it is necessary to call rte_eal_init to complete EAL
>>>>> 	initialization. There is no more merging arguments from different
>>>>> 	sources (cfg file and command line).
>>>>>   	Added non_eal_configure to testpmd application.
>>>>> 	Function maintain the same functionality as rte_eal_configure but
>>>>> 	for non-eal arguments. 
>>>>>   	Added config JSON feature to testpmd last patch from patchset contain
>>>>> 	example showing use of .json configuration files.
>>>>>
>>>>>   lib cfgfile:
>>>>>   	Rework of add_section(), add_entry() new implementation
>>>>>   	New members allocated_entries/sections, free_entries/sections
>>>>> 	in rte_cfgfile structure, change in array of pointers
>>>>> 	**sections, **entries instead of *sections[], *entries[]
>>>>>   	Add  set_entry() to update/overwrite already existing entry in cfgfile
>>>>> 	struct
>>>>>   	Add save() function to save on disc cfgfile structure in INI format
>>>>>   	Rework of existing load() function  simplifying the code
>>>>>   	Add unit test realloc_sections() in TEST app for testing realloc/malloc
>>>>> 	of new API functions, add test for save() function
>>>>>
>>>>> Kuba Kozak (3):
>>>>>   eal: add functions parsing EAL arguments
>>>>>   app/testpmd: add parse options from cfg file
>>>>>   app/testpmd: add parse options from JSON cfg file
>>>>
>>>> This patchset is idle more than a year now.
>>>> It solves problem of eal parameters, it doesn't remove them but at least moves
>>>> from command line to config file.
>>>>
>>>> The patch seems mostly done, but what is the status of it, do we want to
>>>> continue it?
>>>> And if we want to continue it can this be a good candidate for GCOS?
>>>
>>> I think we must focus on reorganization of EAL first.
>>> When the options parsing will be better isolated,
>>> and accessible from API independant of rte_eal_init,
>>> then we could provide some helpers to use those APIs
>>> for a config file, a custom command line or anything else.
>>
>> Is there any actions do we need to take when patches are rejected?
> 
> Not sure I understand your question.
> Any opinion about such plan?

When patch status updated, they will disappear from our radar, should we do
something to remind us this kind of work needs to be done and already some
patches are available to benefit.

Keeping them in the patchwork is one option, but it is getting hard to manage as
the list grows there, and not all work stays relevant/active in the backlog.
Also their status is not clear in the patchwork backlog.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5 0/3] EAL change for using a config file for DPDK
  2019-01-24 14:46                         ` Ferruh Yigit
@ 2019-01-24 16:06                           ` Thomas Monjalon
  2019-01-24 16:18                             ` Ferruh Yigit
  0 siblings, 1 reply; 70+ messages in thread
From: Thomas Monjalon @ 2019-01-24 16:06 UTC (permalink / raw)
  To: Ferruh Yigit
  Cc: Kuba Kozak, deepak.k.jain, bruce.richardson,
	michalx.k.jastrzebski, jacekx.piasecki, dpdk-dev,
	Stephen Hemminger, Kevin Traynor, David Marchand

24/01/2019 15:46, Ferruh Yigit:
> On 1/24/2019 2:32 PM, Thomas Monjalon wrote:
> > 24/01/2019 14:54, Ferruh Yigit:
> >> On 1/23/2019 8:26 PM, Thomas Monjalon wrote:
> >>> 23/01/2019 20:31, Ferruh Yigit:
> >>>> On 7/13/2017 11:07 AM, kubax.kozak at intel.com (Kuba Kozak) wrote:
> >>>>> This patchset introduce a mechanism for running dpdk application with 
> >>>>> parameters provided by configuration file.
> >>>>>
> >>>>> A new API for EAL takes a config file data type - either loaded from
> >>>>> file, or built up programmatically in the application - and extracts
> >>>>> DPDK parameters from it to be used when eal init is called. 
> >>>>> This allows apps to have an alternative method to configure EAL,
> >>>>> other than via command-line parameters.
> >>>>>
> >>>>> Reworked applications are used to demonstrate the new eal API.
> >>>>> If a --cfgfile-path <path> option is passed into command line non
> >>>>> EAL section, then the file is loaded and used by app. If a file
> >>>>> called config.ini is present in current working directory, and
> >>>>> no --cfgfile-path option is passed in, config.ini file will be
> >>>>> loaded and used by app.
> >>>>>
> >>>>> Patch "app/testpmd: add parse options from JSON cfg file" 
> >>>>> demonstrates the usage of JSON instead of INI file format. 
> >>>>> JSON file can be called the same way as above, 
> >>>>> through --cfgfile-path <path> argument.
> >>>>> ---
> >>>>> this patch depends on:
> >>>>> "Rework cfgfile API to enable apps config file support"
> >>>>>
> >>>>> v5:
> >>>>>   changed define "RTE_DEVTYPE_VIRTUAL" to "RTE_DEVTYPE_UNDEFINED"
> >>>>>   due to compilation errors (changes on current master).
> >>>>>
> >>>>> v4:
> >>>>>  Code optimalisation in parse_vdev_devices() function.
> >>>>>  Moved some functions from librte_eal/bsdapp and librte_eal/linuxapp
> >>>>>  to the librte_eal/common.
> >>>>>  Bug fixes.
> >>>>>
> >>>>> v3: 
> >>>>>  split one patchset into two distinct patchsets:
> >>>>>  1. cfgfile library and TEST app changes
> >>>>>  2. EAL changes and examples (this patchset depends on cfgfile)
> >>>>>
> >>>>> v2:
> >>>>>   lib eal:
> >>>>> 	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
> >>>>> 	Now this function load data from cfg structure and did initial
> >>>>> 	initialization of EAL arguments. Vdev argument are stored in different
> >>>>> 	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
> >>>>> 	function it is necessary to call rte_eal_init to complete EAL
> >>>>> 	initialization. There is no more merging arguments from different
> >>>>> 	sources (cfg file and command line).
> >>>>>   	Added non_eal_configure to testpmd application.
> >>>>> 	Function maintain the same functionality as rte_eal_configure but
> >>>>> 	for non-eal arguments. 
> >>>>>   	Added config JSON feature to testpmd last patch from patchset contain
> >>>>> 	example showing use of .json configuration files.
> >>>>>
> >>>>>   lib cfgfile:
> >>>>>   	Rework of add_section(), add_entry() new implementation
> >>>>>   	New members allocated_entries/sections, free_entries/sections
> >>>>> 	in rte_cfgfile structure, change in array of pointers
> >>>>> 	**sections, **entries instead of *sections[], *entries[]
> >>>>>   	Add  set_entry() to update/overwrite already existing entry in cfgfile
> >>>>> 	struct
> >>>>>   	Add save() function to save on disc cfgfile structure in INI format
> >>>>>   	Rework of existing load() function  simplifying the code
> >>>>>   	Add unit test realloc_sections() in TEST app for testing realloc/malloc
> >>>>> 	of new API functions, add test for save() function
> >>>>>
> >>>>> Kuba Kozak (3):
> >>>>>   eal: add functions parsing EAL arguments
> >>>>>   app/testpmd: add parse options from cfg file
> >>>>>   app/testpmd: add parse options from JSON cfg file
> >>>>
> >>>> This patchset is idle more than a year now.
> >>>> It solves problem of eal parameters, it doesn't remove them but at least moves
> >>>> from command line to config file.
> >>>>
> >>>> The patch seems mostly done, but what is the status of it, do we want to
> >>>> continue it?
> >>>> And if we want to continue it can this be a good candidate for GCOS?
> >>>
> >>> I think we must focus on reorganization of EAL first.
> >>> When the options parsing will be better isolated,
> >>> and accessible from API independant of rte_eal_init,
> >>> then we could provide some helpers to use those APIs
> >>> for a config file, a custom command line or anything else.
> >>
> >> Is there any actions do we need to take when patches are rejected?
> > 
> > Not sure I understand your question.
> > Any opinion about such plan?
> 
> When patch status updated, they will disappear from our radar, should we do
> something to remind us this kind of work needs to be done and already some
> patches are available to benefit.
> 
> Keeping them in the patchwork is one option, but it is getting hard to manage as
> the list grows there, and not all work stays relevant/active in the backlog.
> Also their status is not clear in the patchwork backlog.

I think we should add an item in the roadmap with a link to this patch.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5 0/3] EAL change for using a config file for DPDK
  2019-01-24 16:06                           ` Thomas Monjalon
@ 2019-01-24 16:18                             ` Ferruh Yigit
  2019-01-24 17:45                               ` Thomas Monjalon
  0 siblings, 1 reply; 70+ messages in thread
From: Ferruh Yigit @ 2019-01-24 16:18 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Kuba Kozak, deepak.k.jain, bruce.richardson,
	michalx.k.jastrzebski, jacekx.piasecki, dpdk-dev,
	Stephen Hemminger, Kevin Traynor, David Marchand

On 1/24/2019 4:06 PM, Thomas Monjalon wrote:
> 24/01/2019 15:46, Ferruh Yigit:
>> On 1/24/2019 2:32 PM, Thomas Monjalon wrote:
>>> 24/01/2019 14:54, Ferruh Yigit:
>>>> On 1/23/2019 8:26 PM, Thomas Monjalon wrote:
>>>>> 23/01/2019 20:31, Ferruh Yigit:
>>>>>> On 7/13/2017 11:07 AM, kubax.kozak at intel.com (Kuba Kozak) wrote:
>>>>>>> This patchset introduce a mechanism for running dpdk application with 
>>>>>>> parameters provided by configuration file.
>>>>>>>
>>>>>>> A new API for EAL takes a config file data type - either loaded from
>>>>>>> file, or built up programmatically in the application - and extracts
>>>>>>> DPDK parameters from it to be used when eal init is called. 
>>>>>>> This allows apps to have an alternative method to configure EAL,
>>>>>>> other than via command-line parameters.
>>>>>>>
>>>>>>> Reworked applications are used to demonstrate the new eal API.
>>>>>>> If a --cfgfile-path <path> option is passed into command line non
>>>>>>> EAL section, then the file is loaded and used by app. If a file
>>>>>>> called config.ini is present in current working directory, and
>>>>>>> no --cfgfile-path option is passed in, config.ini file will be
>>>>>>> loaded and used by app.
>>>>>>>
>>>>>>> Patch "app/testpmd: add parse options from JSON cfg file" 
>>>>>>> demonstrates the usage of JSON instead of INI file format. 
>>>>>>> JSON file can be called the same way as above, 
>>>>>>> through --cfgfile-path <path> argument.
>>>>>>> ---
>>>>>>> this patch depends on:
>>>>>>> "Rework cfgfile API to enable apps config file support"
>>>>>>>
>>>>>>> v5:
>>>>>>>   changed define "RTE_DEVTYPE_VIRTUAL" to "RTE_DEVTYPE_UNDEFINED"
>>>>>>>   due to compilation errors (changes on current master).
>>>>>>>
>>>>>>> v4:
>>>>>>>  Code optimalisation in parse_vdev_devices() function.
>>>>>>>  Moved some functions from librte_eal/bsdapp and librte_eal/linuxapp
>>>>>>>  to the librte_eal/common.
>>>>>>>  Bug fixes.
>>>>>>>
>>>>>>> v3: 
>>>>>>>  split one patchset into two distinct patchsets:
>>>>>>>  1. cfgfile library and TEST app changes
>>>>>>>  2. EAL changes and examples (this patchset depends on cfgfile)
>>>>>>>
>>>>>>> v2:
>>>>>>>   lib eal:
>>>>>>> 	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
>>>>>>> 	Now this function load data from cfg structure and did initial
>>>>>>> 	initialization of EAL arguments. Vdev argument are stored in different
>>>>>>> 	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
>>>>>>> 	function it is necessary to call rte_eal_init to complete EAL
>>>>>>> 	initialization. There is no more merging arguments from different
>>>>>>> 	sources (cfg file and command line).
>>>>>>>   	Added non_eal_configure to testpmd application.
>>>>>>> 	Function maintain the same functionality as rte_eal_configure but
>>>>>>> 	for non-eal arguments. 
>>>>>>>   	Added config JSON feature to testpmd last patch from patchset contain
>>>>>>> 	example showing use of .json configuration files.
>>>>>>>
>>>>>>>   lib cfgfile:
>>>>>>>   	Rework of add_section(), add_entry() new implementation
>>>>>>>   	New members allocated_entries/sections, free_entries/sections
>>>>>>> 	in rte_cfgfile structure, change in array of pointers
>>>>>>> 	**sections, **entries instead of *sections[], *entries[]
>>>>>>>   	Add  set_entry() to update/overwrite already existing entry in cfgfile
>>>>>>> 	struct
>>>>>>>   	Add save() function to save on disc cfgfile structure in INI format
>>>>>>>   	Rework of existing load() function  simplifying the code
>>>>>>>   	Add unit test realloc_sections() in TEST app for testing realloc/malloc
>>>>>>> 	of new API functions, add test for save() function
>>>>>>>
>>>>>>> Kuba Kozak (3):
>>>>>>>   eal: add functions parsing EAL arguments
>>>>>>>   app/testpmd: add parse options from cfg file
>>>>>>>   app/testpmd: add parse options from JSON cfg file
>>>>>>
>>>>>> This patchset is idle more than a year now.
>>>>>> It solves problem of eal parameters, it doesn't remove them but at least moves
>>>>>> from command line to config file.
>>>>>>
>>>>>> The patch seems mostly done, but what is the status of it, do we want to
>>>>>> continue it?
>>>>>> And if we want to continue it can this be a good candidate for GCOS?
>>>>>
>>>>> I think we must focus on reorganization of EAL first.
>>>>> When the options parsing will be better isolated,
>>>>> and accessible from API independant of rte_eal_init,
>>>>> then we could provide some helpers to use those APIs
>>>>> for a config file, a custom command line or anything else.
>>>>
>>>> Is there any actions do we need to take when patches are rejected?
>>>
>>> Not sure I understand your question.
>>> Any opinion about such plan?
>>
>> When patch status updated, they will disappear from our radar, should we do
>> something to remind us this kind of work needs to be done and already some
>> patches are available to benefit.
>>
>> Keeping them in the patchwork is one option, but it is getting hard to manage as
>> the list grows there, and not all work stays relevant/active in the backlog.
>> Also their status is not clear in the patchwork backlog.
> 
> I think we should add an item in the roadmap with a link to this patch.

OK. Do you want me do it?

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5 0/3] EAL change for using a config file for DPDK
  2019-01-24 16:18                             ` Ferruh Yigit
@ 2019-01-24 17:45                               ` Thomas Monjalon
  2019-01-28 14:43                                 ` Ferruh Yigit
  0 siblings, 1 reply; 70+ messages in thread
From: Thomas Monjalon @ 2019-01-24 17:45 UTC (permalink / raw)
  To: Ferruh Yigit
  Cc: Kuba Kozak, deepak.k.jain, bruce.richardson,
	michalx.k.jastrzebski, jacekx.piasecki, dpdk-dev,
	Stephen Hemminger, Kevin Traynor, David Marchand

24/01/2019 17:18, Ferruh Yigit:
> On 1/24/2019 4:06 PM, Thomas Monjalon wrote:
> > 24/01/2019 15:46, Ferruh Yigit:
> >> On 1/24/2019 2:32 PM, Thomas Monjalon wrote:
> >>> 24/01/2019 14:54, Ferruh Yigit:
> >>>> On 1/23/2019 8:26 PM, Thomas Monjalon wrote:
> >>>>> 23/01/2019 20:31, Ferruh Yigit:
> >>>>>> On 7/13/2017 11:07 AM, kubax.kozak at intel.com (Kuba Kozak) wrote:
> >>>>>>> This patchset introduce a mechanism for running dpdk application with 
> >>>>>>> parameters provided by configuration file.
> >>>>>>>
> >>>>>>> A new API for EAL takes a config file data type - either loaded from
> >>>>>>> file, or built up programmatically in the application - and extracts
> >>>>>>> DPDK parameters from it to be used when eal init is called. 
> >>>>>>> This allows apps to have an alternative method to configure EAL,
> >>>>>>> other than via command-line parameters.
> >>>>>>>
> >>>>>>> Reworked applications are used to demonstrate the new eal API.
> >>>>>>> If a --cfgfile-path <path> option is passed into command line non
> >>>>>>> EAL section, then the file is loaded and used by app. If a file
> >>>>>>> called config.ini is present in current working directory, and
> >>>>>>> no --cfgfile-path option is passed in, config.ini file will be
> >>>>>>> loaded and used by app.
> >>>>>>>
> >>>>>>> Patch "app/testpmd: add parse options from JSON cfg file" 
> >>>>>>> demonstrates the usage of JSON instead of INI file format. 
> >>>>>>> JSON file can be called the same way as above, 
> >>>>>>> through --cfgfile-path <path> argument.
> >>>>>>> ---
> >>>>>>> this patch depends on:
> >>>>>>> "Rework cfgfile API to enable apps config file support"
> >>>>>>>
> >>>>>>> v5:
> >>>>>>>   changed define "RTE_DEVTYPE_VIRTUAL" to "RTE_DEVTYPE_UNDEFINED"
> >>>>>>>   due to compilation errors (changes on current master).
> >>>>>>>
> >>>>>>> v4:
> >>>>>>>  Code optimalisation in parse_vdev_devices() function.
> >>>>>>>  Moved some functions from librte_eal/bsdapp and librte_eal/linuxapp
> >>>>>>>  to the librte_eal/common.
> >>>>>>>  Bug fixes.
> >>>>>>>
> >>>>>>> v3: 
> >>>>>>>  split one patchset into two distinct patchsets:
> >>>>>>>  1. cfgfile library and TEST app changes
> >>>>>>>  2. EAL changes and examples (this patchset depends on cfgfile)
> >>>>>>>
> >>>>>>> v2:
> >>>>>>>   lib eal:
> >>>>>>> 	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
> >>>>>>> 	Now this function load data from cfg structure and did initial
> >>>>>>> 	initialization of EAL arguments. Vdev argument are stored in different
> >>>>>>> 	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
> >>>>>>> 	function it is necessary to call rte_eal_init to complete EAL
> >>>>>>> 	initialization. There is no more merging arguments from different
> >>>>>>> 	sources (cfg file and command line).
> >>>>>>>   	Added non_eal_configure to testpmd application.
> >>>>>>> 	Function maintain the same functionality as rte_eal_configure but
> >>>>>>> 	for non-eal arguments. 
> >>>>>>>   	Added config JSON feature to testpmd last patch from patchset contain
> >>>>>>> 	example showing use of .json configuration files.
> >>>>>>>
> >>>>>>>   lib cfgfile:
> >>>>>>>   	Rework of add_section(), add_entry() new implementation
> >>>>>>>   	New members allocated_entries/sections, free_entries/sections
> >>>>>>> 	in rte_cfgfile structure, change in array of pointers
> >>>>>>> 	**sections, **entries instead of *sections[], *entries[]
> >>>>>>>   	Add  set_entry() to update/overwrite already existing entry in cfgfile
> >>>>>>> 	struct
> >>>>>>>   	Add save() function to save on disc cfgfile structure in INI format
> >>>>>>>   	Rework of existing load() function  simplifying the code
> >>>>>>>   	Add unit test realloc_sections() in TEST app for testing realloc/malloc
> >>>>>>> 	of new API functions, add test for save() function
> >>>>>>>
> >>>>>>> Kuba Kozak (3):
> >>>>>>>   eal: add functions parsing EAL arguments
> >>>>>>>   app/testpmd: add parse options from cfg file
> >>>>>>>   app/testpmd: add parse options from JSON cfg file
> >>>>>>
> >>>>>> This patchset is idle more than a year now.
> >>>>>> It solves problem of eal parameters, it doesn't remove them but at least moves
> >>>>>> from command line to config file.
> >>>>>>
> >>>>>> The patch seems mostly done, but what is the status of it, do we want to
> >>>>>> continue it?
> >>>>>> And if we want to continue it can this be a good candidate for GCOS?
> >>>>>
> >>>>> I think we must focus on reorganization of EAL first.
> >>>>> When the options parsing will be better isolated,
> >>>>> and accessible from API independant of rte_eal_init,
> >>>>> then we could provide some helpers to use those APIs
> >>>>> for a config file, a custom command line or anything else.
> >>>>
> >>>> Is there any actions do we need to take when patches are rejected?
> >>>
> >>> Not sure I understand your question.
> >>> Any opinion about such plan?
> >>
> >> When patch status updated, they will disappear from our radar, should we do
> >> something to remind us this kind of work needs to be done and already some
> >> patches are available to benefit.
> >>
> >> Keeping them in the patchwork is one option, but it is getting hard to manage as
> >> the list grows there, and not all work stays relevant/active in the backlog.
> >> Also their status is not clear in the patchwork backlog.
> > 
> > I think we should add an item in the roadmap with a link to this patch.
> 
> OK. Do you want me do it?

Yes please expose your view in a roadmap patch.
If needed, we can discuss the plan in techboard meeting.

^ permalink raw reply	[flat|nested] 70+ messages in thread

* Re: [PATCH v5 0/3] EAL change for using a config file for DPDK
  2019-01-24 17:45                               ` Thomas Monjalon
@ 2019-01-28 14:43                                 ` Ferruh Yigit
  0 siblings, 0 replies; 70+ messages in thread
From: Ferruh Yigit @ 2019-01-28 14:43 UTC (permalink / raw)
  To: Thomas Monjalon
  Cc: Kuba Kozak, deepak.k.jain, bruce.richardson,
	michalx.k.jastrzebski, jacekx.piasecki, dpdk-dev,
	Stephen Hemminger, Kevin Traynor, David Marchand

On 1/24/2019 5:45 PM, Thomas Monjalon wrote:
> 24/01/2019 17:18, Ferruh Yigit:
>> On 1/24/2019 4:06 PM, Thomas Monjalon wrote:
>>> 24/01/2019 15:46, Ferruh Yigit:
>>>> On 1/24/2019 2:32 PM, Thomas Monjalon wrote:
>>>>> 24/01/2019 14:54, Ferruh Yigit:
>>>>>> On 1/23/2019 8:26 PM, Thomas Monjalon wrote:
>>>>>>> 23/01/2019 20:31, Ferruh Yigit:
>>>>>>>> On 7/13/2017 11:07 AM, kubax.kozak at intel.com (Kuba Kozak) wrote:
>>>>>>>>> This patchset introduce a mechanism for running dpdk application with 
>>>>>>>>> parameters provided by configuration file.
>>>>>>>>>
>>>>>>>>> A new API for EAL takes a config file data type - either loaded from
>>>>>>>>> file, or built up programmatically in the application - and extracts
>>>>>>>>> DPDK parameters from it to be used when eal init is called. 
>>>>>>>>> This allows apps to have an alternative method to configure EAL,
>>>>>>>>> other than via command-line parameters.
>>>>>>>>>
>>>>>>>>> Reworked applications are used to demonstrate the new eal API.
>>>>>>>>> If a --cfgfile-path <path> option is passed into command line non
>>>>>>>>> EAL section, then the file is loaded and used by app. If a file
>>>>>>>>> called config.ini is present in current working directory, and
>>>>>>>>> no --cfgfile-path option is passed in, config.ini file will be
>>>>>>>>> loaded and used by app.
>>>>>>>>>
>>>>>>>>> Patch "app/testpmd: add parse options from JSON cfg file" 
>>>>>>>>> demonstrates the usage of JSON instead of INI file format. 
>>>>>>>>> JSON file can be called the same way as above, 
>>>>>>>>> through --cfgfile-path <path> argument.
>>>>>>>>> ---
>>>>>>>>> this patch depends on:
>>>>>>>>> "Rework cfgfile API to enable apps config file support"
>>>>>>>>>
>>>>>>>>> v5:
>>>>>>>>>   changed define "RTE_DEVTYPE_VIRTUAL" to "RTE_DEVTYPE_UNDEFINED"
>>>>>>>>>   due to compilation errors (changes on current master).
>>>>>>>>>
>>>>>>>>> v4:
>>>>>>>>>  Code optimalisation in parse_vdev_devices() function.
>>>>>>>>>  Moved some functions from librte_eal/bsdapp and librte_eal/linuxapp
>>>>>>>>>  to the librte_eal/common.
>>>>>>>>>  Bug fixes.
>>>>>>>>>
>>>>>>>>> v3: 
>>>>>>>>>  split one patchset into two distinct patchsets:
>>>>>>>>>  1. cfgfile library and TEST app changes
>>>>>>>>>  2. EAL changes and examples (this patchset depends on cfgfile)
>>>>>>>>>
>>>>>>>>> v2:
>>>>>>>>>   lib eal:
>>>>>>>>> 	Rework of rte_eal_configure(struct rte_cfgfile *cfg, char *prgname).
>>>>>>>>> 	Now this function load data from cfg structure and did initial
>>>>>>>>> 	initialization of EAL arguments. Vdev argument are stored in different
>>>>>>>>> 	subsections eg. DPDK.vdev0, DPDK.vdev1 etc. After execution of this
>>>>>>>>> 	function it is necessary to call rte_eal_init to complete EAL
>>>>>>>>> 	initialization. There is no more merging arguments from different
>>>>>>>>> 	sources (cfg file and command line).
>>>>>>>>>   	Added non_eal_configure to testpmd application.
>>>>>>>>> 	Function maintain the same functionality as rte_eal_configure but
>>>>>>>>> 	for non-eal arguments. 
>>>>>>>>>   	Added config JSON feature to testpmd last patch from patchset contain
>>>>>>>>> 	example showing use of .json configuration files.
>>>>>>>>>
>>>>>>>>>   lib cfgfile:
>>>>>>>>>   	Rework of add_section(), add_entry() new implementation
>>>>>>>>>   	New members allocated_entries/sections, free_entries/sections
>>>>>>>>> 	in rte_cfgfile structure, change in array of pointers
>>>>>>>>> 	**sections, **entries instead of *sections[], *entries[]
>>>>>>>>>   	Add  set_entry() to update/overwrite already existing entry in cfgfile
>>>>>>>>> 	struct
>>>>>>>>>   	Add save() function to save on disc cfgfile structure in INI format
>>>>>>>>>   	Rework of existing load() function  simplifying the code
>>>>>>>>>   	Add unit test realloc_sections() in TEST app for testing realloc/malloc
>>>>>>>>> 	of new API functions, add test for save() function
>>>>>>>>>
>>>>>>>>> Kuba Kozak (3):
>>>>>>>>>   eal: add functions parsing EAL arguments
>>>>>>>>>   app/testpmd: add parse options from cfg file
>>>>>>>>>   app/testpmd: add parse options from JSON cfg file
>>>>>>>>
>>>>>>>> This patchset is idle more than a year now.
>>>>>>>> It solves problem of eal parameters, it doesn't remove them but at least moves
>>>>>>>> from command line to config file.
>>>>>>>>
>>>>>>>> The patch seems mostly done, but what is the status of it, do we want to
>>>>>>>> continue it?
>>>>>>>> And if we want to continue it can this be a good candidate for GCOS?
>>>>>>>
>>>>>>> I think we must focus on reorganization of EAL first.
>>>>>>> When the options parsing will be better isolated,
>>>>>>> and accessible from API independant of rte_eal_init,
>>>>>>> then we could provide some helpers to use those APIs
>>>>>>> for a config file, a custom command line or anything else.
>>>>>>
>>>>>> Is there any actions do we need to take when patches are rejected?
>>>>>
>>>>> Not sure I understand your question.
>>>>> Any opinion about such plan?
>>>>
>>>> When patch status updated, they will disappear from our radar, should we do
>>>> something to remind us this kind of work needs to be done and already some
>>>> patches are available to benefit.
>>>>
>>>> Keeping them in the patchwork is one option, but it is getting hard to manage as
>>>> the list grows there, and not all work stays relevant/active in the backlog.
>>>> Also their status is not clear in the patchwork backlog.
>>>
>>> I think we should add an item in the roadmap with a link to this patch.
>>
>> OK. Do you want me do it?
> 
> Yes please expose your view in a roadmap patch.
> If needed, we can discuss the plan in techboard meeting.

sent, https://mails.dpdk.org/archives/web/2019-January/001011.html

^ permalink raw reply	[flat|nested] 70+ messages in thread

end of thread, other threads:[~2019-01-28 14:43 UTC | newest]

Thread overview: 70+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-30  8:30 [PATCH 0/3] Add support for using a config file for DPDK Jacek Piasecki
2017-05-30  8:30 ` [PATCH 1/3] cfgfile: add new API functions Jacek Piasecki
2017-05-31 14:20   ` Bruce Richardson
2017-05-31 14:22     ` Bruce Richardson
2017-06-26 10:59   ` [PATCH v2 0/7] Add support for using a config file for DPDK Jacek Piasecki
2017-06-26 10:59     ` [PATCH v2 1/7] cfgfile: remove EAL dependency Jacek Piasecki
2017-06-26 13:12       ` Dumitrescu, Cristian
2017-06-27 10:26       ` [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Jacek Piasecki
2017-06-27 10:26         ` [PATCH v3 1/4] cfgfile: remove EAL dependency Jacek Piasecki
2017-06-30  9:44           ` Bruce Richardson
2017-06-30 11:16             ` Bruce Richardson
2017-06-27 10:26         ` [PATCH v3 2/4] cfgfile: add new functions to API Jacek Piasecki
2017-06-30  9:55           ` Bruce Richardson
2017-06-30 10:28           ` Bruce Richardson
2017-06-27 10:26         ` [PATCH v3 3/4] cfgfile: rework of load function Jacek Piasecki
2017-06-30 11:18           ` Bruce Richardson
2017-06-27 10:26         ` [PATCH v3 4/4] test/cfgfile: add new unit test Jacek Piasecki
2017-06-30 11:20         ` [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Bruce Richardson
2017-06-26 10:59     ` [PATCH v2 2/7] cfgfile: add new functions to API Jacek Piasecki
2017-06-26 10:59     ` [PATCH v2 3/7] cfgfile: rework of load function Jacek Piasecki
2017-06-26 10:59     ` [PATCH v2 4/7] test/cfgfile: add new unit test Jacek Piasecki
2017-06-26 10:59     ` [PATCH v2 5/7] eal: add functions parsing EAL arguments Jacek Piasecki
2017-06-27 10:52       ` [PATCH v3 0/3] EAL change for using a config file for DPDK Jacek Piasecki
2017-06-27 10:52         ` [PATCH v3 1/3] eal: add functions parsing EAL arguments Jacek Piasecki
2017-06-30 16:04           ` Bruce Richardson
2017-07-10 12:44           ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Jacek Piasecki
2017-07-10 12:44             ` [PATCH v4 1/5] cfgfile: remove EAL dependency Jacek Piasecki
2017-08-30 17:58               ` Bruce Richardson
2017-07-10 12:44             ` [PATCH v4 2/5] cfgfile: change existing API functions Jacek Piasecki
2017-08-30 20:07               ` Bruce Richardson
2017-07-10 12:44             ` [PATCH v4 3/5] cfgfile: add new functions to API Jacek Piasecki
2017-08-30 20:18               ` Bruce Richardson
2017-07-10 12:44             ` [PATCH v4 4/5] cfgfile: rework of load function Jacek Piasecki
2017-08-30 20:24               ` Bruce Richardson
2017-07-10 12:44             ` [PATCH v4 5/5] test/cfgfile: add new unit test Jacek Piasecki
2017-08-30 20:25               ` Bruce Richardson
2017-09-04  9:21                 ` Bruce Richardson
2017-09-04  9:30               ` Bruce Richardson
2017-09-15 13:56                 ` Thomas Monjalon
2017-09-18 13:49                   ` Jastrzebski, MichalX K
2017-07-10 15:13             ` [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Thomas Monjalon
2017-07-20 21:48               ` Thomas Monjalon
2017-07-10 12:51           ` [PATCH v4 0/3] EAL change for using a config file for DPDK Kuba Kozak
2017-07-10 12:51             ` [PATCH v4 1/3] eal: add functions parsing EAL arguments Kuba Kozak
2017-07-13  9:26               ` [PATCH v5 0/3] EAL change for using a config file for DPDK Kuba Kozak
2017-07-13 10:07               ` Kuba Kozak
2017-07-13 10:07                 ` [PATCH v5 1/3] eal: add functions parsing EAL arguments Kuba Kozak
2017-07-13 10:07                 ` [PATCH v5 2/3] app/testpmd: add parse options from cfg file Kuba Kozak
2017-07-13 10:07                 ` [PATCH v5 3/3] app/testpmd: add parse options from JSON " Kuba Kozak
2019-01-23 19:31                 ` [PATCH v5 0/3] EAL change for using a config file for DPDK Ferruh Yigit
2019-01-23 20:26                   ` Thomas Monjalon
2019-01-24 13:54                     ` Ferruh Yigit
2019-01-24 14:32                       ` Thomas Monjalon
2019-01-24 14:46                         ` Ferruh Yigit
2019-01-24 16:06                           ` Thomas Monjalon
2019-01-24 16:18                             ` Ferruh Yigit
2019-01-24 17:45                               ` Thomas Monjalon
2019-01-28 14:43                                 ` Ferruh Yigit
2017-07-10 12:51             ` [PATCH v4 2/3] app/testpmd: add parse options from cfg file Kuba Kozak
2017-07-10 12:51             ` [PATCH v4 3/3] app/testpmd: add parse options from JSON " Kuba Kozak
2017-06-27 10:52         ` [PATCH v3 2/3] app/testpmd: changed example to parse options from " Jacek Piasecki
2017-06-27 10:52         ` [PATCH v3 3/3] app/testpmd: add parse arguments from JSON config file Jacek Piasecki
2017-07-05  0:00         ` [PATCH v3 0/3] EAL change for using a config file for DPDK Thomas Monjalon
2017-06-26 10:59     ` [PATCH v2 6/7] app/testpmd: changed example to parse options from cfg file Jacek Piasecki
2017-06-26 10:59     ` [PATCH v2 7/7] app/testpmd: add parse arguments from JSON config file Jacek Piasecki
2017-05-30  8:30 ` [PATCH 2/3] eal: add functions parsing EAL arguments Jacek Piasecki
2017-05-31 15:46   ` Bruce Richardson
2017-05-30  8:30 ` [PATCH 3/3] app/testpmd: changed example to parse options from cfg file Jacek Piasecki
2017-06-20  2:13   ` Wu, Jingjing
2017-06-20 10:10     ` Kozak, KubaX

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.