DPDK patches and discussions
 help / color / mirror / Atom feed
From: Bruce Richardson <bruce.richardson@intel.com>
To: Jacek Piasecki <jacekx.piasecki@intel.com>
Cc: dev@dpdk.org, deepak.k.jain@intel.com
Subject: Re: [dpdk-dev] [PATCH v3 2/4] cfgfile: add new functions to API
Date: Fri, 30 Jun 2017 11:28:40 +0100	[thread overview]
Message-ID: <20170630102840.GC14776@bricha3-MOBL3.ger.corp.intel.com> (raw)
In-Reply-To: <1498559210-104084-3-git-send-email-jacekx.piasecki@intel.com>

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
> 

  parent reply	other threads:[~2017-06-30 10:28 UTC|newest]

Thread overview: 70+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-05-30  8:30 [dpdk-dev] [PATCH 0/3] Add support for using a config file for DPDK Jacek Piasecki
2017-05-30  8:30 ` [dpdk-dev] [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   ` [dpdk-dev] [PATCH v2 0/7] Add support for using a config file for DPDK Jacek Piasecki
2017-06-26 10:59     ` [dpdk-dev] [PATCH v2 1/7] cfgfile: remove EAL dependency Jacek Piasecki
2017-06-26 13:12       ` Dumitrescu, Cristian
2017-06-27 10:26       ` [dpdk-dev] [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Jacek Piasecki
2017-06-27 10:26         ` [dpdk-dev] [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         ` [dpdk-dev] [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 [this message]
2017-06-27 10:26         ` [dpdk-dev] [PATCH v3 3/4] cfgfile: rework of load function Jacek Piasecki
2017-06-30 11:18           ` Bruce Richardson
2017-06-27 10:26         ` [dpdk-dev] [PATCH v3 4/4] test/cfgfile: add new unit test Jacek Piasecki
2017-06-30 11:20         ` [dpdk-dev] [PATCH v3 0/4] Rework cfgfile API to enable apps config file support Bruce Richardson
2017-06-26 10:59     ` [dpdk-dev] [PATCH v2 2/7] cfgfile: add new functions to API Jacek Piasecki
2017-06-26 10:59     ` [dpdk-dev] [PATCH v2 3/7] cfgfile: rework of load function Jacek Piasecki
2017-06-26 10:59     ` [dpdk-dev] [PATCH v2 4/7] test/cfgfile: add new unit test Jacek Piasecki
2017-06-26 10:59     ` [dpdk-dev] [PATCH v2 5/7] eal: add functions parsing EAL arguments Jacek Piasecki
2017-06-27 10:52       ` [dpdk-dev] [PATCH v3 0/3] EAL change for using a config file for DPDK Jacek Piasecki
2017-06-27 10:52         ` [dpdk-dev] [PATCH v3 1/3] eal: add functions parsing EAL arguments Jacek Piasecki
2017-06-30 16:04           ` Bruce Richardson
2017-07-10 12:44           ` [dpdk-dev] [PATCH v4 0/5] Rework cfgfile API to enable apps config file support Jacek Piasecki
2017-07-10 12:44             ` [dpdk-dev] [PATCH v4 1/5] cfgfile: remove EAL dependency Jacek Piasecki
2017-08-30 17:58               ` Bruce Richardson
2017-07-10 12:44             ` [dpdk-dev] [PATCH v4 2/5] cfgfile: change existing API functions Jacek Piasecki
2017-08-30 20:07               ` Bruce Richardson
2017-07-10 12:44             ` [dpdk-dev] [PATCH v4 3/5] cfgfile: add new functions to API Jacek Piasecki
2017-08-30 20:18               ` Bruce Richardson
2017-07-10 12:44             ` [dpdk-dev] [PATCH v4 4/5] cfgfile: rework of load function Jacek Piasecki
2017-08-30 20:24               ` Bruce Richardson
2017-07-10 12:44             ` [dpdk-dev] [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             ` [dpdk-dev] [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           ` [dpdk-dev] [PATCH v4 0/3] EAL change for using a config file for DPDK Kuba Kozak
2017-07-10 12:51             ` [dpdk-dev] [PATCH v4 1/3] eal: add functions parsing EAL arguments Kuba Kozak
2017-07-13  9:26               ` [dpdk-dev] [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                 ` [dpdk-dev] [PATCH v5 1/3] eal: add functions parsing EAL arguments Kuba Kozak
2017-07-13 10:07                 ` [dpdk-dev] [PATCH v5 2/3] app/testpmd: add parse options from cfg file Kuba Kozak
2017-07-13 10:07                 ` [dpdk-dev] [PATCH v5 3/3] app/testpmd: add parse options from JSON " Kuba Kozak
2019-01-23 19:31                 ` [dpdk-dev] [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             ` [dpdk-dev] [PATCH v4 2/3] app/testpmd: add parse options from cfg file Kuba Kozak
2017-07-10 12:51             ` [dpdk-dev] [PATCH v4 3/3] app/testpmd: add parse options from JSON " Kuba Kozak
2017-06-27 10:52         ` [dpdk-dev] [PATCH v3 2/3] app/testpmd: changed example to parse options from " Jacek Piasecki
2017-06-27 10:52         ` [dpdk-dev] [PATCH v3 3/3] app/testpmd: add parse arguments from JSON config file Jacek Piasecki
2017-07-05  0:00         ` [dpdk-dev] [PATCH v3 0/3] EAL change for using a config file for DPDK Thomas Monjalon
2017-06-26 10:59     ` [dpdk-dev] [PATCH v2 6/7] app/testpmd: changed example to parse options from cfg file Jacek Piasecki
2017-06-26 10:59     ` [dpdk-dev] [PATCH v2 7/7] app/testpmd: add parse arguments from JSON config file Jacek Piasecki
2017-05-30  8:30 ` [dpdk-dev] [PATCH 2/3] eal: add functions parsing EAL arguments Jacek Piasecki
2017-05-31 15:46   ` Bruce Richardson
2017-05-30  8:30 ` [dpdk-dev] [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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170630102840.GC14776@bricha3-MOBL3.ger.corp.intel.com \
    --to=bruce.richardson@intel.com \
    --cc=deepak.k.jain@intel.com \
    --cc=dev@dpdk.org \
    --cc=jacekx.piasecki@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).