DPDK patches and discussions
 help / color / mirror / Atom feed
* [PATCH v3 0/8] add new command line argument parsing library
@ 2023-12-07 16:18 Euan Bourke
  2023-12-07 16:18 ` [PATCH v3 1/8] arg_parser: new library for command line parsing Euan Bourke
                   ` (8 more replies)
  0 siblings, 9 replies; 15+ messages in thread
From: Euan Bourke @ 2023-12-07 16:18 UTC (permalink / raw)
  To: dev; +Cc: Euan Bourke

A recent thread on the mailing list[1] discussed corelist and coremask
parsing and the idea of a new library dedicated to command line parsing
was mentioned[2]. This patchset adds the library, along with the new
APIs, and edits the existing EAL, DLB2 driver and some example
application functions to use these APIs, rather than each implementing
their own copies.

The new APIs work similar to the existing functions in EAL, however
instead of filling a core array like this:
[1, -1, -1, 2, 3] (a non -1 refers to an 'active core' at that index)
It fills it like this:
[0, 3, 4] (with the value at each index being an 'active core').

The new APIs will also return the number of cores contained in the
passed corelist/coremask, so in the above example, 3 would be returned.

New in the v3, is a heuristic parser which searches for key markers in
the core string, returning a enum value based off this search to
indicate if a parameter is likely a coremask or a corelist. This
heuristic function is also wrapped in a parser function allowing
apps to handle both coremasks and corelists simultaneously.

[1] https://mails.dpdk.org/archives/dev/2023-November/280957.html
[2] https://mails.dpdk.org/archives/dev/2023-November/280966.html


v3:
* new 'combined core string parser' and 'heuristic parser'
* changes to eventdev_pipeline and l3fwd-power example applications
* various struct optimisations in arg_parser.c
* fix for windows build relating to RTE_SWAP()
* minor changes for issues flagged during review

v2:
* changes to EAL service core related parsers to call API.
* various optimisations in core_bit related functions in arg_parser.c.
* add lib to list for windows build.
* minor changes for issues flagged during review.

Euan Bourke (8):
  arg_parser: new library for command line parsing
  arg_parser: add new coremask parsing API
  eal: add support for new arg parsing library
  eal: update to service core related parsers
  event/dlb2: add new arg parsing library API support
  arg_parser: added common core string and heuristic parsers
  examples/eventdev_pipeline: update to call arg parser API
  examples/l3fwd-power: update to call arg parser API

 .mailmap                                     |   1 +
 MAINTAINERS                                  |   4 +
 doc/api/doxy-api-index.md                    |   3 +-
 doc/api/doxy-api.conf.in                     |   1 +
 drivers/event/dlb2/dlb2_priv.h               |   4 +-
 drivers/event/dlb2/pf/base/dlb2_resource.c   |  51 ++--
 examples/eventdev_pipeline/main.c            |  65 +----
 examples/eventdev_pipeline/pipeline_common.h |   1 +
 examples/l3fwd-power/perf_core.c             |  51 +---
 lib/arg_parser/arg_parser.c                  | 229 +++++++++++++++
 lib/arg_parser/meson.build                   |   7 +
 lib/arg_parser/rte_arg_parser.h              | 160 +++++++++++
 lib/arg_parser/version.map                   |  13 +
 lib/eal/common/eal_common_options.c          | 285 ++++---------------
 lib/eal/meson.build                          |   2 +-
 lib/meson.build                              |   2 +
 16 files changed, 513 insertions(+), 366 deletions(-)
 create mode 100644 lib/arg_parser/arg_parser.c
 create mode 100644 lib/arg_parser/meson.build
 create mode 100644 lib/arg_parser/rte_arg_parser.h
 create mode 100644 lib/arg_parser/version.map

-- 
2.34.1


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

* [PATCH v3 1/8] arg_parser: new library for command line parsing
  2023-12-07 16:18 [PATCH v3 0/8] add new command line argument parsing library Euan Bourke
@ 2023-12-07 16:18 ` Euan Bourke
  2023-12-07 16:44   ` Bruce Richardson
  2023-12-07 19:32   ` Tyler Retzlaff
  2023-12-07 16:18 ` [PATCH v3 2/8] arg_parser: add new coremask parsing API Euan Bourke
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 15+ messages in thread
From: Euan Bourke @ 2023-12-07 16:18 UTC (permalink / raw)
  To: dev; +Cc: Euan Bourke, Thomas Monjalon, Bruce Richardson

Add a new library to make it easier for eal and other libraries to parse
command line arguments.

The first function in this library is one to parse a corelist string
into an array of individual core ids. The function will then return the
total number of cores described in the corelist.

Signed-off-by: Euan Bourke <euan.bourke@intel.com>
---
 .mailmap                        |   1 +
 MAINTAINERS                     |   4 ++
 doc/api/doxy-api-index.md       |   3 +-
 doc/api/doxy-api.conf.in        |   1 +
 lib/arg_parser/arg_parser.c     | 108 ++++++++++++++++++++++++++++++++
 lib/arg_parser/meson.build      |   7 +++
 lib/arg_parser/rte_arg_parser.h |  66 +++++++++++++++++++
 lib/arg_parser/version.map      |  10 +++
 lib/meson.build                 |   2 +
 9 files changed, 201 insertions(+), 1 deletion(-)
 create mode 100644 lib/arg_parser/arg_parser.c
 create mode 100644 lib/arg_parser/meson.build
 create mode 100644 lib/arg_parser/rte_arg_parser.h
 create mode 100644 lib/arg_parser/version.map

diff --git a/.mailmap b/.mailmap
index ab0742a382..528bc68a30 100644
--- a/.mailmap
+++ b/.mailmap
@@ -379,6 +379,7 @@ Eric Zhang <eric.zhang@windriver.com>
 Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
 Erik Ziegenbalg <eziegenb@brocade.com>
 Erlu Chen <erlu.chen@intel.com>
+Euan Bourke <euan.bourke@intel.com>
 Eugenio Pérez <eperezma@redhat.com>
 Eugeny Parshutin <eugeny.parshutin@linux.intel.com>
 Evan Swanson <evan.swanson@intel.com>
diff --git a/MAINTAINERS b/MAINTAINERS
index 0d1c8126e3..68ef5ba14b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1756,6 +1756,10 @@ M: Nithin Dabilpuram <ndabilpuram@marvell.com>
 M: Pavan Nikhilesh <pbhagavatula@marvell.com>
 F: lib/node/
 
+Argument parsing
+M: Bruce Richardson <bruce.richardson@intel.com>
+F: lib/arg_parser/
+
 
 Test Applications
 -----------------
diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index a6a768bd7c..f711010140 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -221,7 +221,8 @@ The public API headers are grouped by topics:
   [config file](@ref rte_cfgfile.h),
   [key/value args](@ref rte_kvargs.h),
   [string](@ref rte_string_fns.h),
-  [thread](@ref rte_thread.h)
+  [thread](@ref rte_thread.h),
+  [argument parsing](@ref rte_arg_parser.h)
 
 - **debug**:
   [jobstats](@ref rte_jobstats.h),
diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
index e94c9e4e46..05718ba6ed 100644
--- a/doc/api/doxy-api.conf.in
+++ b/doc/api/doxy-api.conf.in
@@ -28,6 +28,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
                           @TOPDIR@/lib/eal/include \
                           @TOPDIR@/lib/eal/include/generic \
                           @TOPDIR@/lib/acl \
+                          @TOPDIR@/lib/arg_parser \
                           @TOPDIR@/lib/bbdev \
                           @TOPDIR@/lib/bitratestats \
                           @TOPDIR@/lib/bpf \
diff --git a/lib/arg_parser/arg_parser.c b/lib/arg_parser/arg_parser.c
new file mode 100644
index 0000000000..240f63d8e1
--- /dev/null
+++ b/lib/arg_parser/arg_parser.c
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Intel Corporation
+ */
+
+#include "errno.h"
+#include "stdlib.h"
+#include "ctype.h"
+#include "string.h"
+#include "stdbool.h"
+
+#include <rte_arg_parser.h>
+#include <rte_common.h>
+
+
+struct core_bits {
+	uint8_t bits[(UINT16_MAX + 1) / CHAR_BIT];
+	uint16_t max_bit_set;
+	uint16_t min_bit_set;
+	uint32_t total_bits_set;
+};
+
+static inline bool
+get_core_bit(struct core_bits *mask, uint16_t idx)
+{
+	return !!(mask->bits[idx / CHAR_BIT] & (1 << (idx % CHAR_BIT)));
+}
+
+static inline void
+set_core_bit(struct core_bits *mask, uint16_t idx)
+{
+	if (get_core_bit(mask, idx))
+		return;
+
+	mask->bits[idx / CHAR_BIT] |= 1 << (idx % CHAR_BIT);
+	/* Update min and max bit if its first time setting a bit */
+	if (++(mask->total_bits_set) == 1) {
+		mask->min_bit_set = idx;
+		mask->max_bit_set = idx;
+		return;
+	}
+
+	if (idx > mask->max_bit_set)
+		mask->max_bit_set = idx;
+
+	if (idx < mask->min_bit_set)
+		mask->min_bit_set = idx;
+}
+
+static inline uint32_t
+corebits_to_array(struct core_bits *mask, uint16_t *cores, size_t max_cores)
+{
+	uint32_t count = 0;
+	for (uint32_t i = mask->min_bit_set; i <= mask->max_bit_set && count < max_cores; i++) {
+		if (get_core_bit(mask, i))
+			cores[count++] = i;
+	}
+	return mask->total_bits_set;
+}
+
+
+int
+rte_arg_parse_corelist(const char *corelist, uint16_t *cores, uint32_t cores_len)
+{
+	struct core_bits mask = {0};
+	int32_t min = -1;
+	char *end = NULL;
+
+	min = -1;
+	do {
+		int64_t idx;
+		int32_t max;
+
+		while (isblank(*corelist))
+			corelist++;
+		if (!isdigit(*corelist))
+			return -1;
+
+		errno = 0;
+		idx = strtol(corelist, &end, 10);
+		if (errno || end == NULL || idx > UINT16_MAX)
+			return -1;
+		while (isblank(*end))
+			end++;
+		if (*end == '-')
+			min = idx;
+
+		else if (*end == ',' || *end == '\0') {
+			if (min == -1)
+				min = max = idx;
+			else if (min > idx) {
+				max = min;
+				min = idx;
+			} else
+				max = idx;
+
+			for (; min <= max; min++)
+				set_core_bit(&mask, min);
+
+			min = -1;
+		} else
+			return -1;
+		corelist = end + 1;
+	} while (*end != '\0');
+
+	uint32_t total_count = corebits_to_array(&mask, cores, cores_len);
+
+	return total_count;
+}
diff --git a/lib/arg_parser/meson.build b/lib/arg_parser/meson.build
new file mode 100644
index 0000000000..6ee228bd69
--- /dev/null
+++ b/lib/arg_parser/meson.build
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2023 Intel Corporation
+
+sources = files('arg_parser.c')
+headers = files('rte_arg_parser.h')
+
+includes += global_inc
diff --git a/lib/arg_parser/rte_arg_parser.h b/lib/arg_parser/rte_arg_parser.h
new file mode 100644
index 0000000000..80579f8cf3
--- /dev/null
+++ b/lib/arg_parser/rte_arg_parser.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2023 Intel Corporation
+ */
+
+#ifndef _RTE_ARG_PARSER_H_
+#define _RTE_ARG_PARSER_H_
+
+/**
+ * @file
+ *
+ * RTE Argument Parsing API
+ *
+ * The argument parsing API is a collection of functions to help parse
+ * command line arguments. The API takes a string input and will return
+ * it to the user in a more usable format.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#include <rte_compat.h>
+
+
+/**
+ * Convert a string describing a list of core ids into an array of core ids.
+ *
+ * On success, the passed array is filled with the core ids present in the list up
+ * to the "cores_len", and the number of unique cores present in the "corelist"
+ * is returned.
+ * For example, passing a 1-3,6 "corelist" results in an array of [1, 2, 3, 6]
+ * and would return 4.
+ *
+ * NOTE: if the length of the input array is insufficient to hold the number of core ids
+ * in "corelist" the input array is filled to capacity but the return value is the
+ * number of elements which would have been written to the array, had enough space been
+ * available. [This is similar to the behaviour of the snprintf function]. Because of
+ * this, the number of core values in the "corelist" may be determined by calling the
+ * function with a NULL array pointer and array length given as 0.
+ *
+ * @param corelist
+ *   Input string describing a list of core ids.
+ * @param cores
+ *   An array where to store the core ids.
+ *   Array can be NULL if "cores_len" is 0.
+ * @param cores_len
+ *   The length of the "cores" array.
+ *   If the size is smaller than that needed to hold all cores from "corelist",
+ *   only "cores_len" elements will be written to the array.
+ * @return
+ *   n: the number of unique cores present in "corelist".
+ *   -1 if the string was invalid.
+ *   NOTE: if n > "cores_len", then only "cores_len" elements in the "cores" array are valid.
+ */
+__rte_experimental
+int
+rte_arg_parse_corelist(const char *corelist, uint16_t *cores, uint32_t cores_len);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RTE_ARG_PARSER_H_ */
diff --git a/lib/arg_parser/version.map b/lib/arg_parser/version.map
new file mode 100644
index 0000000000..b0caaac569
--- /dev/null
+++ b/lib/arg_parser/version.map
@@ -0,0 +1,10 @@
+DPDK_24 {
+	local: *;
+};
+
+EXPERIMENTAL {
+	global:
+
+	# added in 24.03
+	rte_arg_parse_corelist;
+};
diff --git a/lib/meson.build b/lib/meson.build
index 6c143ce5a6..db9e769033 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -11,6 +11,7 @@
 libraries = [
         'log',
         'kvargs', # eal depends on kvargs
+        'arg_parser',
         'telemetry', # basic info querying
         'eal', # everything depends on eal
         'ring',
@@ -72,6 +73,7 @@ if is_ms_compiler
             'log',
             'kvargs',
             'telemetry',
+            'arg_parser',
     ]
 endif
 
-- 
2.34.1


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

* [PATCH v3 2/8] arg_parser: add new coremask parsing API
  2023-12-07 16:18 [PATCH v3 0/8] add new command line argument parsing library Euan Bourke
  2023-12-07 16:18 ` [PATCH v3 1/8] arg_parser: new library for command line parsing Euan Bourke
@ 2023-12-07 16:18 ` Euan Bourke
  2023-12-07 16:18 ` [PATCH v3 3/8] eal: add support for new arg parsing library Euan Bourke
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Euan Bourke @ 2023-12-07 16:18 UTC (permalink / raw)
  To: dev; +Cc: Euan Bourke, Bruce Richardson

Add new coremask parsing API. This API behaves similarly
to the corelist parsing API, taking a coremask string, a cores
array and a cores_len int. Parsing the coremask string, filling
its values into the cores array up to cores_len.

The API also returns a 'count' which corresponds to the total number
of cores in the coremask string.

Signed-off-by: Euan Bourke <euan.bourke@intel.com>
---
 lib/arg_parser/arg_parser.c     | 53 +++++++++++++++++++++++++++++++++
 lib/arg_parser/rte_arg_parser.h | 34 +++++++++++++++++++++
 lib/arg_parser/version.map      |  1 +
 3 files changed, 88 insertions(+)

diff --git a/lib/arg_parser/arg_parser.c b/lib/arg_parser/arg_parser.c
index 240f63d8e1..cebab9e2f8 100644
--- a/lib/arg_parser/arg_parser.c
+++ b/lib/arg_parser/arg_parser.c
@@ -11,6 +11,9 @@
 #include <rte_arg_parser.h>
 #include <rte_common.h>
 
+#define BITS_PER_HEX 4
+#define MAX_COREMASK_SIZE ((UINT16_MAX + 1) / BITS_PER_HEX)
+
 
 struct core_bits {
 	uint8_t bits[(UINT16_MAX + 1) / CHAR_BIT];
@@ -57,6 +60,15 @@ corebits_to_array(struct core_bits *mask, uint16_t *cores, size_t max_cores)
 	return mask->total_bits_set;
 }
 
+static int xdigit2val(unsigned char c)
+{
+	if (isdigit(c))
+		return c - '0';
+	else if (isupper(c))
+		return c - 'A' + 10;
+	else
+		return c - 'a' + 10;
+}
 
 int
 rte_arg_parse_corelist(const char *corelist, uint16_t *cores, uint32_t cores_len)
@@ -106,3 +118,44 @@ rte_arg_parse_corelist(const char *corelist, uint16_t *cores, uint32_t cores_len
 
 	return total_count;
 }
+
+int
+rte_arg_parse_coremask(const char *coremask, uint16_t *cores, uint32_t cores_len)
+{
+	struct core_bits mask = {0};
+
+	/* Remove all blank characters ahead and after .
+	 * Remove 0x/0X if exists.
+	 */
+	while (isblank(*coremask))
+		coremask++;
+	if (coremask[0] == '0' && ((coremask[1] == 'x') || (coremask[1] == 'X')))
+		coremask += 2;
+
+	int32_t i = strlen(coremask);
+	while ((i > 0) && isblank(coremask[i - 1]))
+		i--;
+	if (i == 0 || i > MAX_COREMASK_SIZE)
+		return -1;
+
+	uint32_t idx = 0;
+
+	for (i = i - 1; i >= 0; i--) {
+		int val;
+		char c = coremask[i];
+
+		if (isxdigit(c) == 0)
+			return -1;
+
+		val = xdigit2val(c);
+
+		for (uint8_t j = 0; j < BITS_PER_HEX; j++, idx++) {
+			if ((1 << j) & val)
+				set_core_bit(&mask, idx);
+		}
+	}
+
+	uint32_t total_count = corebits_to_array(&mask, cores, cores_len);
+
+	return total_count;
+}
diff --git a/lib/arg_parser/rte_arg_parser.h b/lib/arg_parser/rte_arg_parser.h
index 80579f8cf3..359d40e305 100644
--- a/lib/arg_parser/rte_arg_parser.h
+++ b/lib/arg_parser/rte_arg_parser.h
@@ -58,6 +58,40 @@ __rte_experimental
 int
 rte_arg_parse_corelist(const char *corelist, uint16_t *cores, uint32_t cores_len);
 
+/**
+ * Convert a string describing a bitmask of core ids into an array of core ids.
+ *
+ * On success, the passed array is filled with the core ids present in the
+ * bitmask up to the "cores_len", and the number of unique cores present in the
+ * "coremask" is returned.
+ * For example, passing a 0xA "coremask" results in an array of [1, 3]
+ * and would return 2.
+ *
+ * NOTE: if the length of the input array is insufficient to hold the number of core ids
+ * in "coremask" the input array is filled to capacity but the return value is the
+ * number of elements which would have been written to the array, had enough space been
+ * available. [This is similar to the behaviour of the snprintf function]. Because of
+ * this, the number of core values in the "coremask" may be determined by calling the
+ * function with a NULL array pointer and array length given as 0.
+ *
+ * @param coremask
+ *   A string containing a bitmask of core ids.
+ * @param cores
+ *   An array where to store the core ids.
+ *   Array can be NULL if "cores_len" is 0.
+ * @param cores_len
+ *   The length of the "cores" array.
+ *   If the size is smaller than that needed to hold all cores from "coremask",
+ *   only "cores_len" elements will be written to the array.
+ * @return
+ *   n: the number of unique cores present in "coremask".
+ *   -1 if the string was invalid.
+ *   NOTE: if n > "cores_len", then only "cores_len" elements in the "cores" array are valid.
+ */
+__rte_experimental
+int
+rte_arg_parse_coremask(const char *coremask, uint16_t *cores, uint32_t cores_len);
+
 
 #ifdef __cplusplus
 }
diff --git a/lib/arg_parser/version.map b/lib/arg_parser/version.map
index b0caaac569..b44d4b02b7 100644
--- a/lib/arg_parser/version.map
+++ b/lib/arg_parser/version.map
@@ -7,4 +7,5 @@ EXPERIMENTAL {
 
 	# added in 24.03
 	rte_arg_parse_corelist;
+	rte_arg_parse_coremask;
 };
-- 
2.34.1


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

* [PATCH v3 3/8] eal: add support for new arg parsing library
  2023-12-07 16:18 [PATCH v3 0/8] add new command line argument parsing library Euan Bourke
  2023-12-07 16:18 ` [PATCH v3 1/8] arg_parser: new library for command line parsing Euan Bourke
  2023-12-07 16:18 ` [PATCH v3 2/8] arg_parser: add new coremask parsing API Euan Bourke
@ 2023-12-07 16:18 ` Euan Bourke
  2023-12-07 16:18 ` [PATCH v3 4/8] eal: update to service core related parsers Euan Bourke
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Euan Bourke @ 2023-12-07 16:18 UTC (permalink / raw)
  To: dev; +Cc: Euan Bourke

Update to eal functions relating to corelist and coremask
parsing to support the new arg parsing library. Functions
now call the API instead of implementing their own version.

Signed-off-by: Euan Bourke <euan.bourke@intel.com>
---
 lib/eal/common/eal_common_options.c | 114 ++++------------------------
 lib/eal/meson.build                 |   2 +-
 2 files changed, 14 insertions(+), 102 deletions(-)

diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c
index a6d21f1cba..60ba12a368 100644
--- a/lib/eal/common/eal_common_options.c
+++ b/lib/eal/common/eal_common_options.c
@@ -30,6 +30,7 @@
 #include <rte_tailq.h>
 #include <rte_version.h>
 #include <rte_devargs.h>
+#include <rte_arg_parser.h>
 #include <rte_memcpy.h>
 #ifndef RTE_EXEC_ENV_WINDOWS
 #include <rte_telemetry.h>
@@ -706,7 +707,7 @@ update_lcore_config(int *cores)
 }
 
 static int
-check_core_list(int *lcores, unsigned int count)
+check_core_list(uint16_t *lcores, unsigned int count)
 {
 	char lcorestr[RTE_MAX_LCORE * 10];
 	bool overflow = false;
@@ -746,60 +747,18 @@ check_core_list(int *lcores, unsigned int count)
 int
 rte_eal_parse_coremask(const char *coremask, int *cores)
 {
-	const char *coremask_orig = coremask;
-	int lcores[RTE_MAX_LCORE];
-	unsigned int count = 0;
-	int i, j, idx;
-	int val;
-	char c;
+	int count;
+	uint16_t lcores[RTE_MAX_LCORE];
+	int idx;
 
 	for (idx = 0; idx < RTE_MAX_LCORE; idx++)
 		cores[idx] = -1;
-	idx = 0;
 
-	/* Remove all blank characters ahead and after .
-	 * Remove 0x/0X if exists.
-	 */
-	while (isblank(*coremask))
-		coremask++;
-	if (coremask[0] == '0' && ((coremask[1] == 'x')
-		|| (coremask[1] == 'X')))
-		coremask += 2;
-	i = strlen(coremask);
-	while ((i > 0) && isblank(coremask[i - 1]))
-		i--;
-	if (i == 0) {
-		RTE_LOG(ERR, EAL, "No lcores in coremask: [%s]\n",
-			coremask_orig);
-		return -1;
-	}
+	/* Call public coremask parsing API */
+	count = rte_arg_parse_coremask(coremask, lcores, RTE_MAX_LCORE);
 
-	for (i = i - 1; i >= 0; i--) {
-		c = coremask[i];
-		if (isxdigit(c) == 0) {
-			/* invalid characters */
-			RTE_LOG(ERR, EAL, "invalid characters in coremask: [%s]\n",
-				coremask_orig);
-			return -1;
-		}
-		val = xdigit2val(c);
-		for (j = 0; j < BITS_PER_HEX; j++, idx++)
-		{
-			if ((1 << j) & val) {
-				if (count >= RTE_MAX_LCORE) {
-					RTE_LOG(ERR, EAL, "Too many lcores provided. Cannot exceed RTE_MAX_LCORE (%d)\n",
-						RTE_MAX_LCORE);
-					return -1;
-				}
-				lcores[count++] = idx;
-			}
-		}
-	}
-	if (count == 0) {
-		RTE_LOG(ERR, EAL, "No lcores in coremask: [%s]\n",
-			coremask_orig);
+	if (count <= 0 || count > RTE_MAX_LCORE)
 		return -1;
-	}
 
 	if (check_core_list(lcores, count))
 		return -1;
@@ -898,64 +857,17 @@ eal_parse_service_corelist(const char *corelist)
 static int
 eal_parse_corelist(const char *corelist, int *cores)
 {
-	unsigned int count = 0, i;
-	int lcores[RTE_MAX_LCORE];
-	char *end = NULL;
-	int min, max;
+	int count;
+	uint16_t lcores[RTE_MAX_LCORE];
 	int idx;
 
 	for (idx = 0; idx < RTE_MAX_LCORE; idx++)
 		cores[idx] = -1;
 
-	/* Remove all blank characters ahead */
-	while (isblank(*corelist))
-		corelist++;
+	/* Call public corelist parsing API */
+	count = rte_arg_parse_corelist(corelist, lcores, RTE_MAX_LCORE);
 
-	/* Get list of cores */
-	min = -1;
-	do {
-		while (isblank(*corelist))
-			corelist++;
-		if (*corelist == '\0')
-			return -1;
-		errno = 0;
-		idx = strtol(corelist, &end, 10);
-		if (errno || end == NULL)
-			return -1;
-		if (idx < 0)
-			return -1;
-		while (isblank(*end))
-			end++;
-		if (*end == '-') {
-			min = idx;
-		} else if ((*end == ',') || (*end == '\0')) {
-			max = idx;
-			if (min == -1)
-				min = idx;
-			for (idx = min; idx <= max; idx++) {
-				bool dup = false;
-
-				/* Check if this idx is already present */
-				for (i = 0; i < count; i++) {
-					if (lcores[i] == idx)
-						dup = true;
-				}
-				if (dup)
-					continue;
-				if (count >= RTE_MAX_LCORE) {
-					RTE_LOG(ERR, EAL, "Too many lcores provided. Cannot exceed RTE_MAX_LCORE (%d)\n",
-						RTE_MAX_LCORE);
-					return -1;
-				}
-				lcores[count++] = idx;
-			}
-			min = -1;
-		} else
-			return -1;
-		corelist = end + 1;
-	} while (*end != '\0');
-
-	if (count == 0)
+	if (count <= 0 || count > RTE_MAX_LCORE)
 		return -1;
 
 	if (check_core_list(lcores, count))
diff --git a/lib/eal/meson.build b/lib/eal/meson.build
index e1d6c4cf17..52a22e2e66 100644
--- a/lib/eal/meson.build
+++ b/lib/eal/meson.build
@@ -14,7 +14,7 @@ subdir(exec_env)
 
 subdir(arch_subdir)
 
-deps += ['log', 'kvargs']
+deps += ['log', 'kvargs', 'arg_parser']
 if not is_windows
     deps += ['telemetry']
 endif
-- 
2.34.1


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

* [PATCH v3 4/8] eal: update to service core related parsers
  2023-12-07 16:18 [PATCH v3 0/8] add new command line argument parsing library Euan Bourke
                   ` (2 preceding siblings ...)
  2023-12-07 16:18 ` [PATCH v3 3/8] eal: add support for new arg parsing library Euan Bourke
@ 2023-12-07 16:18 ` Euan Bourke
  2023-12-07 16:18 ` [PATCH v3 5/8] event/dlb2: add new arg parsing library API support Euan Bourke
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Euan Bourke @ 2023-12-07 16:18 UTC (permalink / raw)
  To: dev; +Cc: Euan Bourke

Updates to the parse service cores functions in EAL to call the arg
parser API instead of implementing its own versions.

Signed-off-by: Euan Bourke <euan.bourke@intel.com>
---
 lib/eal/common/eal_common_options.c | 171 +++++++---------------------
 1 file changed, 41 insertions(+), 130 deletions(-)

diff --git a/lib/eal/common/eal_common_options.c b/lib/eal/common/eal_common_options.c
index 60ba12a368..d44654f621 100644
--- a/lib/eal/common/eal_common_options.c
+++ b/lib/eal/common/eal_common_options.c
@@ -573,97 +573,49 @@ eal_plugins_init(void)
  * the global configuration (core role and core count) with the parsed
  * value.
  */
-static int xdigit2val(unsigned char c)
-{
-	int val;
-
-	if (isdigit(c))
-		val = c - '0';
-	else if (isupper(c))
-		val = c - 'A' + 10;
-	else
-		val = c - 'a' + 10;
-	return val;
-}
-
 static int
 eal_parse_service_coremask(const char *coremask)
 {
 	struct rte_config *cfg = rte_eal_get_configuration();
-	int i, j, idx = 0;
-	unsigned int count = 0;
-	char c;
-	int val;
+	uint16_t cores[RTE_MAX_LCORE];
+	int64_t count;
 	uint32_t taken_lcore_count = 0;
 
-	if (coremask == NULL)
-		return -1;
-	/* Remove all blank characters ahead and after .
-	 * Remove 0x/0X if exists.
-	 */
-	while (isblank(*coremask))
-		coremask++;
-	if (coremask[0] == '0' && ((coremask[1] == 'x')
-		|| (coremask[1] == 'X')))
-		coremask += 2;
-	i = strlen(coremask);
-	while ((i > 0) && isblank(coremask[i - 1]))
-		i--;
-
-	if (i == 0)
+	count = rte_arg_parse_coremask(coremask, cores, RTE_DIM(cores));
+
+	if (count == 0 || count == -1)
 		return -1;
 
-	for (i = i - 1; i >= 0 && idx < RTE_MAX_LCORE; i--) {
-		c = coremask[i];
-		if (isxdigit(c) == 0) {
-			/* invalid characters */
+	for (int i = 0; i < count; i++) {
+		uint32_t lcore = cores[i];
+		if (main_lcore_parsed &&
+				cfg->main_lcore == lcore) {
+			RTE_LOG(ERR, EAL,
+				"lcore %u is main lcore, cannot use as service core\n",
+				cores[i]);
 			return -1;
 		}
-		val = xdigit2val(c);
-		for (j = 0; j < BITS_PER_HEX && idx < RTE_MAX_LCORE;
-				j++, idx++) {
-			if ((1 << j) & val) {
-				/* handle main lcore already parsed */
-				uint32_t lcore = idx;
-				if (main_lcore_parsed &&
-						cfg->main_lcore == lcore) {
-					RTE_LOG(ERR, EAL,
-						"lcore %u is main lcore, cannot use as service core\n",
-						idx);
-					return -1;
-				}
 
-				if (eal_cpu_detected(idx) == 0) {
-					RTE_LOG(ERR, EAL,
-						"lcore %u unavailable\n", idx);
-					return -1;
-				}
-
-				if (cfg->lcore_role[idx] == ROLE_RTE)
-					taken_lcore_count++;
-
-				lcore_config[idx].core_role = ROLE_SERVICE;
-				count++;
-			}
-		}
-	}
-
-	for (; i >= 0; i--)
-		if (coremask[i] != '0')
+		if (eal_cpu_detected(cores[i]) == 0) {
+			RTE_LOG(ERR, EAL,
+				"lcore %u unavailable\n", cores[i]);
 			return -1;
+		}
 
-	for (; idx < RTE_MAX_LCORE; idx++)
-		lcore_config[idx].core_index = -1;
-
-	if (count == 0)
-		return -1;
+		if (cfg->lcore_role[cores[i]] == ROLE_RTE)
+			taken_lcore_count++;
 
+		lcore_config[cores[i]].core_role = ROLE_SERVICE;
+	}
 	if (core_parsed && taken_lcore_count != count) {
 		RTE_LOG(WARNING, EAL,
-			"Not all service cores are in the coremask. "
+			"Not all service cores were in the coremask. "
 			"Please ensure -c or -l includes service cores\n");
 	}
 
+	for (uint16_t j = count*4; j < RTE_MAX_LCORE; j++)
+		lcore_config[j].core_index = -1;
+
 	cfg->service_lcore_count = count;
 	return 0;
 }
@@ -780,70 +732,29 @@ static int
 eal_parse_service_corelist(const char *corelist)
 {
 	struct rte_config *cfg = rte_eal_get_configuration();
-	int i;
-	unsigned count = 0;
-	char *end = NULL;
-	uint32_t min, max, idx;
+	int64_t i, count;
+	uint16_t cores[RTE_MAX_LCORE];
 	uint32_t taken_lcore_count = 0;
 
-	if (corelist == NULL)
-		return -1;
+	count = rte_arg_parse_corelist(corelist, cores, RTE_DIM(cores));
 
-	/* Remove all blank characters ahead and after */
-	while (isblank(*corelist))
-		corelist++;
-	i = strlen(corelist);
-	while ((i > 0) && isblank(corelist[i - 1]))
-		i--;
+	if (count == 0 || count == -1)
+		return -1;
 
-	/* Get list of cores */
-	min = RTE_MAX_LCORE;
-	do {
-		while (isblank(*corelist))
-			corelist++;
-		if (*corelist == '\0')
-			return -1;
-		errno = 0;
-		idx = strtoul(corelist, &end, 10);
-		if (errno || end == NULL)
-			return -1;
-		if (idx >= RTE_MAX_LCORE)
-			return -1;
-		while (isblank(*end))
-			end++;
-		if (*end == '-') {
-			min = idx;
-		} else if ((*end == ',') || (*end == '\0')) {
-			max = idx;
-			if (min == RTE_MAX_LCORE)
-				min = idx;
-			for (idx = min; idx <= max; idx++) {
-				if (cfg->lcore_role[idx] != ROLE_SERVICE) {
-					/* handle main lcore already parsed */
-					uint32_t lcore = idx;
-					if (cfg->main_lcore == lcore &&
-							main_lcore_parsed) {
-						RTE_LOG(ERR, EAL,
-							"Error: lcore %u is main lcore, cannot use as service core\n",
-							idx);
-						return -1;
-					}
-					if (cfg->lcore_role[idx] == ROLE_RTE)
-						taken_lcore_count++;
-
-					lcore_config[idx].core_role =
-							ROLE_SERVICE;
-					count++;
-				}
-			}
-			min = RTE_MAX_LCORE;
-		} else
+	for (i = 0; i < count; i++) {
+		uint32_t lcore = cores[i];
+		if (cfg->main_lcore == lcore &&
+				main_lcore_parsed) {
+			RTE_LOG(ERR, EAL,
+				"Error: lcore %u is main lcore, cannot use as service core\n",
+				cores[i]);
 			return -1;
-		corelist = end + 1;
-	} while (*end != '\0');
+		}
+		if (cfg->lcore_role[cores[i]] == ROLE_RTE)
+			taken_lcore_count++;
 
-	if (count == 0)
-		return -1;
+		lcore_config[cores[i]].core_role = ROLE_SERVICE;
+	}
 
 	if (core_parsed && taken_lcore_count != count) {
 		RTE_LOG(WARNING, EAL,
-- 
2.34.1


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

* [PATCH v3 5/8] event/dlb2: add new arg parsing library API support
  2023-12-07 16:18 [PATCH v3 0/8] add new command line argument parsing library Euan Bourke
                   ` (3 preceding siblings ...)
  2023-12-07 16:18 ` [PATCH v3 4/8] eal: update to service core related parsers Euan Bourke
@ 2023-12-07 16:18 ` Euan Bourke
  2023-12-07 16:18 ` [PATCH v3 6/8] arg_parser: added common core string and heuristic parsers Euan Bourke
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Euan Bourke @ 2023-12-07 16:18 UTC (permalink / raw)
  To: dev; +Cc: Euan Bourke, Abdullah Sevincer

Switched the dlb2 driver to call the new arg parsing library instead of
eal for coremask parsing, and updated the resource probe function to
support the changed formatting of the API.

Signed-off-by: Euan Bourke <euan.bourke@intel.com>
---
 drivers/event/dlb2/dlb2_priv.h             |  4 +-
 drivers/event/dlb2/pf/base/dlb2_resource.c | 51 +++++++++-------------
 2 files changed, 21 insertions(+), 34 deletions(-)

diff --git a/drivers/event/dlb2/dlb2_priv.h b/drivers/event/dlb2/dlb2_priv.h
index 31a3beeb6c..c14d83da5b 100644
--- a/drivers/event/dlb2/dlb2_priv.h
+++ b/drivers/event/dlb2/dlb2_priv.h
@@ -10,6 +10,7 @@
 
 #include <rte_eventdev.h>
 #include <rte_config.h>
+#include <rte_arg_parser.h>
 #include "dlb2_user.h"
 #include "dlb2_log.h"
 #include "rte_pmd_dlb2.h"
@@ -729,9 +730,6 @@ void dlb2_event_build_hcws(struct dlb2_port *qm_port,
 			   uint8_t *sched_type,
 			   uint8_t *queue_id);
 
-/* Extern functions */
-extern int rte_eal_parse_coremask(const char *coremask, int *cores);
-
 /* Extern globals */
 extern struct process_local_port_data dlb2_port[][DLB2_NUM_PORT_TYPES];
 
diff --git a/drivers/event/dlb2/pf/base/dlb2_resource.c b/drivers/event/dlb2/pf/base/dlb2_resource.c
index 7ce3e3531c..b65de9350a 100644
--- a/drivers/event/dlb2/pf/base/dlb2_resource.c
+++ b/drivers/event/dlb2/pf/base/dlb2_resource.c
@@ -922,49 +922,38 @@ dlb2_resource_probe(struct dlb2_hw *hw, const void *probe_args)
 {
 	const struct dlb2_devargs *args = (const struct dlb2_devargs *)probe_args;
 	const char *mask = args ? args->producer_coremask : NULL;
-	int cpu = 0, cnt = 0, cores[RTE_MAX_LCORE], i;
+	int cpu = 0, i;
+	uint16_t cores[RTE_MAX_LCORE];
 
 	if (args) {
 		mask = (const char *)args->producer_coremask;
 	}
 
-	if (mask && rte_eal_parse_coremask(mask, cores)) {
+	int ret = rte_arg_parse_coremask(mask, cores, RTE_DIM(cores));
+
+	if (mask && ret == -1) {
 		DLB2_LOG_ERR(": Invalid producer coremask=%s", mask);
 		return -1;
 	}
 
-	hw->num_prod_cores = 0;
-	for (i = 0; i < RTE_MAX_LCORE; i++) {
-		bool is_pcore = (mask && cores[i] != -1);
-
-		if (rte_lcore_is_enabled(i)) {
-			if (is_pcore) {
-				/*
-				 * Populate the producer cores from parsed
-				 * coremask
-				 */
-				hw->prod_core_list[cores[i]] = i;
-				hw->num_prod_cores++;
-
-			} else if ((++cnt == DLB2_EAL_PROBE_CORE ||
-			   rte_lcore_count() < DLB2_EAL_PROBE_CORE)) {
-				/*
-				 * If no producer coremask is provided, use the
-				 * second EAL core to probe
-				 */
-				cpu = i;
-				break;
-			}
-		} else if (is_pcore) {
+	hw->num_prod_cores = ret;
+	/* Check for no producer cores and then get the second EAL core */
+	if (hw->num_prod_cores > 0)
+		cpu = cores[0];
+	else if (rte_lcore_count() < DLB2_EAL_PROBE_CORE)
+		cpu = rte_get_main_lcore();
+	else
+		cpu = rte_get_next_lcore(-1, 1, 0);
+
+	/* check our producer list is valid and error out if not */
+	for (i = 0; i < hw->num_prod_cores; i++) {
+		if (!rte_lcore_is_enabled(cores[i])) {
 			DLB2_LOG_ERR("Producer coremask(%s) must be a subset of EAL coremask",
-				     mask);
+				mask);
 			return -1;
-		}
-
 	}
-	/* Use the first core in producer coremask to probe */
-	if (hw->num_prod_cores)
-		cpu = hw->prod_core_list[0];
+	hw->prod_core_list[i] = cores[i];
+}
 
 	dlb2_get_pp_allocation(hw, cpu, DLB2_LDB_PORT);
 	dlb2_get_pp_allocation(hw, cpu, DLB2_DIR_PORT);
-- 
2.34.1


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

* [PATCH v3 6/8] arg_parser: added common core string and heuristic parsers
  2023-12-07 16:18 [PATCH v3 0/8] add new command line argument parsing library Euan Bourke
                   ` (4 preceding siblings ...)
  2023-12-07 16:18 ` [PATCH v3 5/8] event/dlb2: add new arg parsing library API support Euan Bourke
@ 2023-12-07 16:18 ` Euan Bourke
  2023-12-07 16:58   ` Bruce Richardson
  2023-12-07 16:18 ` [PATCH v3 7/8] examples/eventdev_pipeline: update to call arg parser API Euan Bourke
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: Euan Bourke @ 2023-12-07 16:18 UTC (permalink / raw)
  To: dev; +Cc: Euan Bourke, Bruce Richardson

Two new functions, the first is a 'heuristic parser' which examines a
string describing a set of cores and determines based off heuristics
whether its a coremask or a corelist.

Second is a 'combined parser' which calls the first function and then
based off the returned value will call the relevant core string parser.
This function also takes a 'default_type' int which corresponds to
which parser should be used in the case of an ambiguous string.

Signed-off-by: Euan Bourke <euan.bourke@intel.com>
---
 lib/arg_parser/arg_parser.c     | 68 +++++++++++++++++++++++++++++++++
 lib/arg_parser/rte_arg_parser.h | 60 +++++++++++++++++++++++++++++
 lib/arg_parser/version.map      |  2 +
 3 files changed, 130 insertions(+)

diff --git a/lib/arg_parser/arg_parser.c b/lib/arg_parser/arg_parser.c
index cebab9e2f8..95cbc50c13 100644
--- a/lib/arg_parser/arg_parser.c
+++ b/lib/arg_parser/arg_parser.c
@@ -7,10 +7,15 @@
 #include "ctype.h"
 #include "string.h"
 #include "stdbool.h"
+#include "stdio.h"
 
 #include <rte_arg_parser.h>
 #include <rte_common.h>
 
+#define RTE_ARG_PARSE_TYPE_COREMASK 0
+#define RTE_ARG_PARSE_TYPE_CORELIST 1
+#define RTE_ARG_PARSE_TYPE_UNKNOWN 2
+
 #define BITS_PER_HEX 4
 #define MAX_COREMASK_SIZE ((UINT16_MAX + 1) / BITS_PER_HEX)
 
@@ -22,6 +27,7 @@ struct core_bits {
 	uint32_t total_bits_set;
 };
 
+
 static inline bool
 get_core_bit(struct core_bits *mask, uint16_t idx)
 {
@@ -159,3 +165,65 @@ rte_arg_parse_coremask(const char *coremask, uint16_t *cores, uint32_t cores_len
 
 	return total_count;
 }
+
+int
+rte_arg_parse_arg_type(const char *core_string)
+{
+	/* Remove leading whitespace */
+	while (isblank(*core_string))
+		core_string++;
+
+	/* Check for 0x prefix */
+	if (core_string[0] == '0' && tolower(core_string[1]) == 'x') {
+		if (core_string[2] != '\0')
+			return RTE_ARG_PARSE_TYPE_COREMASK;
+		return -1;
+	}
+
+	int i = 0, idx = 0;
+	/* Check for ',' and '-' and check for A-F */
+	do {
+		while (isblank(core_string[idx]))
+			idx++;
+
+		if (core_string[idx] == ',' || core_string[idx] == '-')
+			return RTE_ARG_PARSE_TYPE_CORELIST;
+
+		if (isalpha(core_string[idx])) {
+			if (isxdigit(core_string[idx]))
+				return RTE_ARG_PARSE_TYPE_COREMASK;
+			return -1;
+		}
+		idx++;
+		i++;
+	} while (core_string[idx] != '\0');
+
+	/* Check length of core_string if ambiguous as max length of a uint16_t is 5 digits
+	 * implying its a coremask.
+	 */
+	if (i > 5)
+		return RTE_ARG_PARSE_TYPE_COREMASK;
+
+	return -1;
+}
+
+int
+rte_arg_parse_core_string(const char *core_string, uint16_t *cores, uint32_t cores_len,
+		int default_type)
+{
+	if (default_type != RTE_ARG_PARSE_TYPE_COREMASK &&
+			default_type != RTE_ARG_PARSE_TYPE_CORELIST) {
+		return -1;
+	}
+	switch (rte_arg_parse_arg_type(core_string)) {
+	case RTE_ARG_PARSE_TYPE_COREMASK:
+		return rte_arg_parse_coremask(core_string, cores, cores_len);
+	case RTE_ARG_PARSE_TYPE_CORELIST:
+		return rte_arg_parse_corelist(core_string, cores, cores_len);
+	default:
+		return default_type == RTE_ARG_PARSE_TYPE_COREMASK ?
+			rte_arg_parse_coremask(core_string, cores, cores_len) :
+			rte_arg_parse_corelist(core_string, cores, cores_len);
+		return -1;
+	}
+}
diff --git a/lib/arg_parser/rte_arg_parser.h b/lib/arg_parser/rte_arg_parser.h
index 359d40e305..125ca9524c 100644
--- a/lib/arg_parser/rte_arg_parser.h
+++ b/lib/arg_parser/rte_arg_parser.h
@@ -92,6 +92,66 @@ __rte_experimental
 int
 rte_arg_parse_coremask(const char *coremask, uint16_t *cores, uint32_t cores_len);
 
+/**
+ * Use heuristics to determine if a string contains a coremask or a corelist.
+ *
+ * This function will check a series of conditions and return an int representing which
+ * core type (mask or list) the string represents or UNKNOWN if the string is ambiguous.
+ *
+ * @param core_string
+ *   A string describing the intended cores to be parsed
+ * @return
+ *   int representing the core type
+ *   -1: error.
+ *   0: coremask.
+ *   1: corelist.
+ *   2: unknown (ambiguous).
+ */
+__rte_experimental
+int
+rte_arg_parse_arg_type(const char *core_string);
+
+/**
+ * Convert a string describing either a corelist or coremask into an array of core ids.
+ *
+ * This function will fill the "cores" array up to "cores_len" with the core ids described
+ * in the "core_string". The string can either describe a corelist or a coremask, and
+ * will be parsed accordingly. The number of unique core ids in the string is then returned.
+ * For example:
+ * "1-4" is treated as a corelist and results in an array of [1,2,3,4] with 4 being returned
+ * "0xA1" is treated as a coremask and results in an array of [0,5,7] with 3 being returned
+ *
+ * In the case of an ambiguous string, the function will use the default_type parameter to
+ * decide.
+ *
+ * NOTE: if the length of the input array is insufficient to hold the number of core ids
+ * in "core_string" the input array is filled to capacity but the return value is the
+ * number of elements which would have been written to the array, had enough space been
+ * available. [This is similar to the behaviour of the snprintf function]. Because of
+ * this, the number of core values in the "core_string" may be determined by calling the
+ * function with a NULL array pointer and array length given as 0.
+ *
+ * @param core_string
+ *   A string describing the intended cores to be parsed.
+ * @param cores
+ *   An array where to store the core ids.
+ *   Array can be NULL if "cores_len" is 0.
+ * @param cores_len
+ *   The length of the "cores" array.
+ *   If the size is smaller than that needed to hold all cores from "core_string"
+ * @param default_type
+ *   How to treat ambiguous cases (e.g. '4' could be mask or list).
+ *   0: mask.
+ *   1: list.
+ * @return
+ *   n: the number of unique cores present in "core_string".
+ *   -1 if the string was invalid.
+ *   NOTE: if n > "cores_len", then only "cores_len" elements in the "cores" array are valid.
+ */
+__rte_experimental
+int
+rte_arg_parse_core_string(const char *core_string, uint16_t *cores, uint32_t cores_len,
+		int default_type);
 
 #ifdef __cplusplus
 }
diff --git a/lib/arg_parser/version.map b/lib/arg_parser/version.map
index b44d4b02b7..383b6bd0e9 100644
--- a/lib/arg_parser/version.map
+++ b/lib/arg_parser/version.map
@@ -8,4 +8,6 @@ EXPERIMENTAL {
 	# added in 24.03
 	rte_arg_parse_corelist;
 	rte_arg_parse_coremask;
+	rte_arg_parse_arg_type;
+	rte_arg_parse_core_string;
 };
-- 
2.34.1


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

* [PATCH v3 7/8] examples/eventdev_pipeline: update to call arg parser API
  2023-12-07 16:18 [PATCH v3 0/8] add new command line argument parsing library Euan Bourke
                   ` (5 preceding siblings ...)
  2023-12-07 16:18 ` [PATCH v3 6/8] arg_parser: added common core string and heuristic parsers Euan Bourke
@ 2023-12-07 16:18 ` Euan Bourke
  2023-12-07 16:18 ` [PATCH v3 8/8] examples/l3fwd-power: " Euan Bourke
  2023-12-07 17:34 ` [PATCH v3 0/8] add new command line argument parsing library Stephen Hemminger
  8 siblings, 0 replies; 15+ messages in thread
From: Euan Bourke @ 2023-12-07 16:18 UTC (permalink / raw)
  To: dev; +Cc: Euan Bourke, Harry van Haaren

Update to the eventdev_pipeline example application to call the arg parser
library for its 'combined core string parser' instead of implementing its
own coremask parser. The default_type passed into the function call is
a coremask.

Signed-off-by: Euan Bourke <euan.bourke@intel.com>
---
 examples/eventdev_pipeline/main.c            | 65 +++-----------------
 examples/eventdev_pipeline/pipeline_common.h |  1 +
 2 files changed, 10 insertions(+), 56 deletions(-)

diff --git a/examples/eventdev_pipeline/main.c b/examples/eventdev_pipeline/main.c
index 0c995d1a70..c59d01e7a5 100644
--- a/examples/eventdev_pipeline/main.c
+++ b/examples/eventdev_pipeline/main.c
@@ -56,69 +56,22 @@ core_in_use(unsigned int lcore_id) {
 		fdata->tx_core[lcore_id] || fdata->worker_core[lcore_id]);
 }
 
-/*
- * Parse the coremask given as argument (hexadecimal string) and fill
- * the global configuration (core role and core count) with the parsed
- * value.
- */
-static int xdigit2val(unsigned char c)
-{
-	int val;
-
-	if (isdigit(c))
-		val = c - '0';
-	else if (isupper(c))
-		val = c - 'A' + 10;
-	else
-		val = c - 'a' + 10;
-	return val;
-}
-
 static uint64_t
 parse_coremask(const char *coremask)
 {
-	int i, j, idx = 0;
-	unsigned int count = 0;
-	char c;
-	int val;
+	int count;
+	uint16_t i;
 	uint64_t mask = 0;
-	const int32_t BITS_HEX = 4;
+	uint16_t cores[RTE_MAX_LCORE];
 
-	if (coremask == NULL)
-		return -1;
-	/* Remove all blank characters ahead and after .
-	 * Remove 0x/0X if exists.
-	 */
-	while (isblank(*coremask))
-		coremask++;
-	if (coremask[0] == '0' && ((coremask[1] == 'x')
-		|| (coremask[1] == 'X')))
-		coremask += 2;
-	i = strlen(coremask);
-	while ((i > 0) && isblank(coremask[i - 1]))
-		i--;
-	if (i == 0)
-		return -1;
+	count = rte_arg_parse_core_string(coremask, cores, RTE_DIM(cores), 0);
 
-	for (i = i - 1; i >= 0 && idx < MAX_NUM_CORE; i--) {
-		c = coremask[i];
-		if (isxdigit(c) == 0) {
-			/* invalid characters */
-			return -1;
-		}
-		val = xdigit2val(c);
-		for (j = 0; j < BITS_HEX && idx < MAX_NUM_CORE; j++, idx++) {
-			if ((1 << j) & val) {
-				mask |= (1ULL << idx);
-				count++;
-			}
-		}
-	}
-	for (; i >= 0; i--)
-		if (coremask[i] != '0')
-			return -1;
-	if (count == 0)
+	if (count == 0 || count == -1)
 		return -1;
+
+	for (i = 0; i < count; i++)
+		mask |= 1ULL << cores[i];
+
 	return mask;
 }
 
diff --git a/examples/eventdev_pipeline/pipeline_common.h b/examples/eventdev_pipeline/pipeline_common.h
index 28b6ab85ff..2b97a29bfc 100644
--- a/examples/eventdev_pipeline/pipeline_common.h
+++ b/examples/eventdev_pipeline/pipeline_common.h
@@ -6,6 +6,7 @@
 
 #include <stdbool.h>
 
+#include <rte_arg_parser.h>
 #include <rte_eal.h>
 #include <rte_mempool.h>
 #include <rte_mbuf.h>
-- 
2.34.1


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

* [PATCH v3 8/8] examples/l3fwd-power: update to call arg parser API
  2023-12-07 16:18 [PATCH v3 0/8] add new command line argument parsing library Euan Bourke
                   ` (6 preceding siblings ...)
  2023-12-07 16:18 ` [PATCH v3 7/8] examples/eventdev_pipeline: update to call arg parser API Euan Bourke
@ 2023-12-07 16:18 ` Euan Bourke
  2023-12-11 12:01   ` Hunt, David
  2023-12-11 16:50   ` Tummala, Sivaprasad
  2023-12-07 17:34 ` [PATCH v3 0/8] add new command line argument parsing library Stephen Hemminger
  8 siblings, 2 replies; 15+ messages in thread
From: Euan Bourke @ 2023-12-07 16:18 UTC (permalink / raw)
  To: dev; +Cc: Euan Bourke, Anatoly Burakov, David Hunt, Sivaprasad Tummala

Update to the l3fwd-power example application to call the arg parser
library for its 'combined core string parser' instead of implementing its
own corelist parser. The default_type passed into the function call is
a corelist.

Signed-off-by: Euan Bourke <euan.bourke@intel.com>
---
 examples/l3fwd-power/perf_core.c | 51 +++++---------------------------
 1 file changed, 8 insertions(+), 43 deletions(-)

diff --git a/examples/l3fwd-power/perf_core.c b/examples/l3fwd-power/perf_core.c
index 41ef6d0c9a..f8511e30b3 100644
--- a/examples/l3fwd-power/perf_core.c
+++ b/examples/l3fwd-power/perf_core.c
@@ -12,6 +12,7 @@
 #include <rte_lcore.h>
 #include <rte_power.h>
 #include <rte_string_fns.h>
+#include <rte_arg_parser.h>
 
 #include "perf_core.h"
 #include "main.h"
@@ -177,56 +178,20 @@ parse_perf_config(const char *q_arg)
 int
 parse_perf_core_list(const char *corelist)
 {
-	int i, idx = 0;
-	unsigned int count = 0;
-	char *end = NULL;
-	int min, max;
+	int count;
+	uint16_t cores[RTE_MAX_LCORE];
 
 	if (corelist == NULL) {
 		printf("invalid core list\n");
 		return -1;
 	}
 
+	count = rte_arg_parse_core_string(corelist, cores, RTE_DIM(cores), 1);
 
-	/* Remove all blank characters ahead and after */
-	while (isblank(*corelist))
-		corelist++;
-	i = strlen(corelist);
-	while ((i > 0) && isblank(corelist[i - 1]))
-		i--;
+	for (int i = 0; i < count; i++)
+		hp_lcores[i] = cores[i];
 
-	/* Get list of cores */
-	min = RTE_MAX_LCORE;
-	do {
-		while (isblank(*corelist))
-			corelist++;
-		if (*corelist == '\0')
-			return -1;
-		errno = 0;
-		idx = strtoul(corelist, &end, 10);
-		if (errno || end == NULL)
-			return -1;
-		while (isblank(*end))
-			end++;
-		if (*end == '-') {
-			min = idx;
-		} else if ((*end == ',') || (*end == '\0')) {
-			max = idx;
-			if (min == RTE_MAX_LCORE)
-				min = idx;
-			for (idx = min; idx <= max; idx++) {
-				hp_lcores[count] = idx;
-				count++;
-			}
-			min = RTE_MAX_LCORE;
-		} else {
-			printf("invalid core list\n");
-			return -1;
-		}
-		corelist = end + 1;
-	} while (*end != '\0');
-
-	if (count == 0) {
+	if (count == 0 || count == -1) {
 		printf("invalid core list\n");
 		return -1;
 	}
@@ -234,7 +199,7 @@ parse_perf_core_list(const char *corelist)
 	nb_hp_lcores = count;
 
 	printf("Configured %d high performance cores\n", nb_hp_lcores);
-	for (i = 0; i < nb_hp_lcores; i++)
+	for (uint16_t i = 0; i < nb_hp_lcores; i++)
 		printf("\tHigh performance core %d %d\n",
 				i, hp_lcores[i]);
 
-- 
2.34.1


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

* Re: [PATCH v3 1/8] arg_parser: new library for command line parsing
  2023-12-07 16:18 ` [PATCH v3 1/8] arg_parser: new library for command line parsing Euan Bourke
@ 2023-12-07 16:44   ` Bruce Richardson
  2023-12-07 19:32   ` Tyler Retzlaff
  1 sibling, 0 replies; 15+ messages in thread
From: Bruce Richardson @ 2023-12-07 16:44 UTC (permalink / raw)
  To: Euan Bourke; +Cc: dev, Thomas Monjalon

On Thu, Dec 07, 2023 at 04:18:11PM +0000, Euan Bourke wrote:
> Add a new library to make it easier for eal and other libraries to parse
> command line arguments.
> 
> The first function in this library is one to parse a corelist string
> into an array of individual core ids. The function will then return the
> total number of cores described in the corelist.
> 
> Signed-off-by: Euan Bourke <euan.bourke@intel.com>

There are other patches outstanding on the list for argument parsing too
[1], and we need to reconcile what goes into what libraries with what names!
Probably they can all be merged into one, but we need to check.

[1] http://patches.dpdk.org/project/dpdk/list/?series=30439


Some minor comments inline below for any v4.

> ---
>  .mailmap                        |   1 +
>  MAINTAINERS                     |   4 ++
>  doc/api/doxy-api-index.md       |   3 +-
>  doc/api/doxy-api.conf.in        |   1 +
>  lib/arg_parser/arg_parser.c     | 108 ++++++++++++++++++++++++++++++++
>  lib/arg_parser/meson.build      |   7 +++
>  lib/arg_parser/rte_arg_parser.h |  66 +++++++++++++++++++
>  lib/arg_parser/version.map      |  10 +++
>  lib/meson.build                 |   2 +
>  9 files changed, 201 insertions(+), 1 deletion(-)
>  create mode 100644 lib/arg_parser/arg_parser.c
>  create mode 100644 lib/arg_parser/meson.build
>  create mode 100644 lib/arg_parser/rte_arg_parser.h
>  create mode 100644 lib/arg_parser/version.map
> 
> diff --git a/.mailmap b/.mailmap
> index ab0742a382..528bc68a30 100644
> --- a/.mailmap
> +++ b/.mailmap
> @@ -379,6 +379,7 @@ Eric Zhang <eric.zhang@windriver.com>
>  Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
>  Erik Ziegenbalg <eziegenb@brocade.com>
>  Erlu Chen <erlu.chen@intel.com>
> +Euan Bourke <euan.bourke@intel.com>
>  Eugenio Pérez <eperezma@redhat.com>
>  Eugeny Parshutin <eugeny.parshutin@linux.intel.com>
>  Evan Swanson <evan.swanson@intel.com>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0d1c8126e3..68ef5ba14b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1756,6 +1756,10 @@ M: Nithin Dabilpuram <ndabilpuram@marvell.com>
>  M: Pavan Nikhilesh <pbhagavatula@marvell.com>
>  F: lib/node/
>  
> +Argument parsing
> +M: Bruce Richardson <bruce.richardson@intel.com>
> +F: lib/arg_parser/
> +
>  
>  Test Applications
>  -----------------
> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> index a6a768bd7c..f711010140 100644
> --- a/doc/api/doxy-api-index.md
> +++ b/doc/api/doxy-api-index.md
> @@ -221,7 +221,8 @@ The public API headers are grouped by topics:
>    [config file](@ref rte_cfgfile.h),
>    [key/value args](@ref rte_kvargs.h),
>    [string](@ref rte_string_fns.h),
> -  [thread](@ref rte_thread.h)
> +  [thread](@ref rte_thread.h),
> +  [argument parsing](@ref rte_arg_parser.h)

Not sure what order, if any, these elements are in, but I think argument
parsing could well be further up the list. It also reduces the diff by one
line, since you don't need to append the comma on the end of rte_thread
line.

>  
>  - **debug**:
>    [jobstats](@ref rte_jobstats.h),
> diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
> index e94c9e4e46..05718ba6ed 100644
> --- a/doc/api/doxy-api.conf.in
> +++ b/doc/api/doxy-api.conf.in
> @@ -28,6 +28,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
>                            @TOPDIR@/lib/eal/include \
>                            @TOPDIR@/lib/eal/include/generic \
>                            @TOPDIR@/lib/acl \
> +                          @TOPDIR@/lib/arg_parser \
>                            @TOPDIR@/lib/bbdev \
>                            @TOPDIR@/lib/bitratestats \
>                            @TOPDIR@/lib/bpf \
> diff --git a/lib/arg_parser/arg_parser.c b/lib/arg_parser/arg_parser.c
> new file mode 100644
> index 0000000000..240f63d8e1
> --- /dev/null
> +++ b/lib/arg_parser/arg_parser.c
> @@ -0,0 +1,108 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2023 Intel Corporation
> + */
> +
> +#include "errno.h"
> +#include "stdlib.h"
> +#include "ctype.h"
> +#include "string.h"
> +#include "stdbool.h"
> +
> +#include <rte_arg_parser.h>
> +#include <rte_common.h>
> +
> +
> +struct core_bits {
> +	uint8_t bits[(UINT16_MAX + 1) / CHAR_BIT];
> +	uint16_t max_bit_set;
> +	uint16_t min_bit_set;
> +	uint32_t total_bits_set;
> +};
> +
> +static inline bool
> +get_core_bit(struct core_bits *mask, uint16_t idx)
> +{
> +	return !!(mask->bits[idx / CHAR_BIT] & (1 << (idx % CHAR_BIT)));
> +}
> +
> +static inline void
> +set_core_bit(struct core_bits *mask, uint16_t idx)
> +{
> +	if (get_core_bit(mask, idx))
> +		return;
> +
> +	mask->bits[idx / CHAR_BIT] |= 1 << (idx % CHAR_BIT);
> +	/* Update min and max bit if its first time setting a bit */
> +	if (++(mask->total_bits_set) == 1) {
> +		mask->min_bit_set = idx;
> +		mask->max_bit_set = idx;
> +		return;
> +	}
> +
> +	if (idx > mask->max_bit_set)
> +		mask->max_bit_set = idx;
> +
> +	if (idx < mask->min_bit_set)
> +		mask->min_bit_set = idx;
> +}
> +
> +static inline uint32_t
> +corebits_to_array(struct core_bits *mask, uint16_t *cores, size_t max_cores)
> +{
> +	uint32_t count = 0;
> +	for (uint32_t i = mask->min_bit_set; i <= mask->max_bit_set && count < max_cores; i++) {
> +		if (get_core_bit(mask, i))
> +			cores[count++] = i;
> +	}
> +	return mask->total_bits_set;
> +}
> +
> +
> +int
> +rte_arg_parse_corelist(const char *corelist, uint16_t *cores, uint32_t cores_len)
> +{
> +	struct core_bits mask = {0};
> +	int32_t min = -1;
> +	char *end = NULL;
> +
> +	min = -1;
> +	do {
> +		int64_t idx;
> +		int32_t max;
> +
> +		while (isblank(*corelist))
> +			corelist++;
> +		if (!isdigit(*corelist))
> +			return -1;
> +
> +		errno = 0;
> +		idx = strtol(corelist, &end, 10);
> +		if (errno || end == NULL || idx > UINT16_MAX)
> +			return -1;
> +		while (isblank(*end))
> +			end++;
> +		if (*end == '-')
> +			min = idx;
> +
> +		else if (*end == ',' || *end == '\0') {
> +			if (min == -1)
> +				min = max = idx;
> +			else if (min > idx) {
> +				max = min;
> +				min = idx;
> +			} else
> +				max = idx;
> + 

This set of if-else branches could do with a few comments.

The first case, min == -1, is for when we get a bare number without a
range, right? The second is when the range is reversed, e.g. 6-3, rather
than 3-6, and the last is for normal ranges. A one-line comment in each leg
of the condition would make this clearer for anyone editing it in future.

> +			for (; min <= max; min++)
> +				set_core_bit(&mask, min);
> +
> +			min = -1;
> +		} else
> +			return -1;
> +		corelist = end + 1;
> +	} while (*end != '\0');
> +
> +	uint32_t total_count = corebits_to_array(&mask, cores, cores_len);
> +
> +	return total_count;

Since you no longer have the bitmask on the heap, total_count is
unnecessary since there is no "free" call.
You can just do "return corebits_to_array(...)"

> +}
> diff --git a/lib/arg_parser/meson.build b/lib/arg_parser/meson.build
> new file mode 100644
> index 0000000000..6ee228bd69
> --- /dev/null
> +++ b/lib/arg_parser/meson.build
> @@ -0,0 +1,7 @@
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2023 Intel Corporation
> +
> +sources = files('arg_parser.c')
> +headers = files('rte_arg_parser.h')
> +
> +includes += global_inc
> diff --git a/lib/arg_parser/rte_arg_parser.h b/lib/arg_parser/rte_arg_parser.h
> new file mode 100644
> index 0000000000..80579f8cf3
> --- /dev/null
> +++ b/lib/arg_parser/rte_arg_parser.h
> @@ -0,0 +1,66 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2023 Intel Corporation
> + */
> +
> +#ifndef _RTE_ARG_PARSER_H_
> +#define _RTE_ARG_PARSER_H_
> +
> +/**
> + * @file
> + *
> + * RTE Argument Parsing API
> + *
> + * The argument parsing API is a collection of functions to help parse
> + * command line arguments. The API takes a string input and will return
> + * it to the user in a more usable format.
> + */
> +
> +#ifdef __cplusplus
> +extern "C" {
> +#endif
> +
> +#include <stdint.h>
> +
> +#include <rte_compat.h>
> +
> +
> +/**
> + * Convert a string describing a list of core ids into an array of core ids.
> + *
> + * On success, the passed array is filled with the core ids present in the list up
> + * to the "cores_len", and the number of unique cores present in the "corelist"
> + * is returned.
> + * For example, passing a 1-3,6 "corelist" results in an array of [1, 2, 3, 6]
> + * and would return 4.
> + *
> + * NOTE: if the length of the input array is insufficient to hold the number of core ids
> + * in "corelist" the input array is filled to capacity but the return value is the
> + * number of elements which would have been written to the array, had enough space been
> + * available. [This is similar to the behaviour of the snprintf function]. Because of
> + * this, the number of core values in the "corelist" may be determined by calling the
> + * function with a NULL array pointer and array length given as 0.
> + *
> + * @param corelist
> + *   Input string describing a list of core ids.
> + * @param cores
> + *   An array where to store the core ids.
> + *   Array can be NULL if "cores_len" is 0.
> + * @param cores_len
> + *   The length of the "cores" array.
> + *   If the size is smaller than that needed to hold all cores from "corelist",
> + *   only "cores_len" elements will be written to the array.
> + * @return
> + *   n: the number of unique cores present in "corelist".
> + *   -1 if the string was invalid.
> + *   NOTE: if n > "cores_len", then only "cores_len" elements in the "cores" array are valid.
> + */
> +__rte_experimental
> +int
> +rte_arg_parse_corelist(const char *corelist, uint16_t *cores, uint32_t cores_len);
> +
> +
> +#ifdef __cplusplus
> +}
> +#endif
> +
> +#endif /* _RTE_ARG_PARSER_H_ */
> diff --git a/lib/arg_parser/version.map b/lib/arg_parser/version.map
> new file mode 100644
> index 0000000000..b0caaac569
> --- /dev/null
> +++ b/lib/arg_parser/version.map
> @@ -0,0 +1,10 @@
> +DPDK_24 {
> +	local: *;
> +};
> +
> +EXPERIMENTAL {
> +	global:
> +
> +	# added in 24.03
> +	rte_arg_parse_corelist;
> +};
> diff --git a/lib/meson.build b/lib/meson.build
> index 6c143ce5a6..db9e769033 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -11,6 +11,7 @@
>  libraries = [
>          'log',
>          'kvargs', # eal depends on kvargs
> +        'arg_parser',
>          'telemetry', # basic info querying
>          'eal', # everything depends on eal
>          'ring',
> @@ -72,6 +73,7 @@ if is_ms_compiler
>              'log',
>              'kvargs',
>              'telemetry',
> +            'arg_parser',
>      ]
>  endif
>  
> -- 
> 2.34.1
> 

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

* Re: [PATCH v3 6/8] arg_parser: added common core string and heuristic parsers
  2023-12-07 16:18 ` [PATCH v3 6/8] arg_parser: added common core string and heuristic parsers Euan Bourke
@ 2023-12-07 16:58   ` Bruce Richardson
  0 siblings, 0 replies; 15+ messages in thread
From: Bruce Richardson @ 2023-12-07 16:58 UTC (permalink / raw)
  To: Euan Bourke; +Cc: dev

On Thu, Dec 07, 2023 at 04:18:16PM +0000, Euan Bourke wrote:
> Two new functions, the first is a 'heuristic parser' which examines a
> string describing a set of cores and determines based off heuristics
> whether its a coremask or a corelist.
> 
> Second is a 'combined parser' which calls the first function and then
> based off the returned value will call the relevant core string parser.
> This function also takes a 'default_type' int which corresponds to
> which parser should be used in the case of an ambiguous string.
> 
> Signed-off-by: Euan Bourke <euan.bourke@intel.com>
> ---
>  lib/arg_parser/arg_parser.c     | 68 +++++++++++++++++++++++++++++++++
>  lib/arg_parser/rte_arg_parser.h | 60 +++++++++++++++++++++++++++++
>  lib/arg_parser/version.map      |  2 +
>  3 files changed, 130 insertions(+)
> 
> diff --git a/lib/arg_parser/arg_parser.c b/lib/arg_parser/arg_parser.c
> index cebab9e2f8..95cbc50c13 100644
> --- a/lib/arg_parser/arg_parser.c
> +++ b/lib/arg_parser/arg_parser.c
> @@ -7,10 +7,15 @@
>  #include "ctype.h"
>  #include "string.h"
>  #include "stdbool.h"
> +#include "stdio.h"
>  
>  #include <rte_arg_parser.h>
>  #include <rte_common.h>
>  
> +#define RTE_ARG_PARSE_TYPE_COREMASK 0
> +#define RTE_ARG_PARSE_TYPE_CORELIST 1
> +#define RTE_ARG_PARSE_TYPE_UNKNOWN 2
> +

As these are used as return values, they need to be defined in the header
file so that applications can use them.

>  #define BITS_PER_HEX 4
>  #define MAX_COREMASK_SIZE ((UINT16_MAX + 1) / BITS_PER_HEX)
>  
> @@ -22,6 +27,7 @@ struct core_bits {
>  	uint32_t total_bits_set;
>  };
>  
> +

Stray newline added to patch.

>  static inline bool
>  get_core_bit(struct core_bits *mask, uint16_t idx)
>  {
> @@ -159,3 +165,65 @@ rte_arg_parse_coremask(const char *coremask, uint16_t *cores, uint32_t cores_len
>  
>  	return total_count;
>  }
> +
> +int
> +rte_arg_parse_arg_type(const char *core_string)
> +{
> +	/* Remove leading whitespace */
> +	while (isblank(*core_string))
> +		core_string++;
> +
> +	/* Check for 0x prefix */
> +	if (core_string[0] == '0' && tolower(core_string[1]) == 'x') {
> +		if (core_string[2] != '\0')
> +			return RTE_ARG_PARSE_TYPE_COREMASK;
> +		return -1;
> +	}
> +
> +	int i = 0, idx = 0;
> +	/* Check for ',' and '-' and check for A-F */
> +	do {
> +		while (isblank(core_string[idx]))
> +			idx++;
> +
> +		if (core_string[idx] == ',' || core_string[idx] == '-')
> +			return RTE_ARG_PARSE_TYPE_CORELIST;
> +
> +		if (isalpha(core_string[idx])) {
> +			if (isxdigit(core_string[idx]))
> +				return RTE_ARG_PARSE_TYPE_COREMASK;
> +			return -1;
> +		}
> +		idx++;
> +		i++;
> +	} while (core_string[idx] != '\0');
> +
> +	/* Check length of core_string if ambiguous as max length of a uint16_t is 5 digits
> +	 * implying its a coremask.
> +	 */
> +	if (i > 5)
> +		return RTE_ARG_PARSE_TYPE_COREMASK;
> +
> +	return -1;

Rather than returning -1, I think in most/all cases above, the function
should return -EINVAL as error code, since it's invalid input passed.

> +}
> +
> +int
> +rte_arg_parse_core_string(const char *core_string, uint16_t *cores, uint32_t cores_len,
> +		int default_type)
> +{
> +	if (default_type != RTE_ARG_PARSE_TYPE_COREMASK &&
> +			default_type != RTE_ARG_PARSE_TYPE_CORELIST) {
> +		return -1;
> +	}
> +	switch (rte_arg_parse_arg_type(core_string)) {
> +	case RTE_ARG_PARSE_TYPE_COREMASK:
> +		return rte_arg_parse_coremask(core_string, cores, cores_len);
> +	case RTE_ARG_PARSE_TYPE_CORELIST:
> +		return rte_arg_parse_corelist(core_string, cores, cores_len);
> +	default:
> +		return default_type == RTE_ARG_PARSE_TYPE_COREMASK ?
> +			rte_arg_parse_coremask(core_string, cores, cores_len) :
> +			rte_arg_parse_corelist(core_string, cores, cores_len);
> +		return -1;
> +	}
> +}
> diff --git a/lib/arg_parser/rte_arg_parser.h b/lib/arg_parser/rte_arg_parser.h
> index 359d40e305..125ca9524c 100644
> --- a/lib/arg_parser/rte_arg_parser.h
> +++ b/lib/arg_parser/rte_arg_parser.h
> @@ -92,6 +92,66 @@ __rte_experimental
>  int
>  rte_arg_parse_coremask(const char *coremask, uint16_t *cores, uint32_t cores_len);
>  
> +/**
> + * Use heuristics to determine if a string contains a coremask or a corelist.
> + *
> + * This function will check a series of conditions and return an int representing which
> + * core type (mask or list) the string represents or UNKNOWN if the string is ambiguous.

"or report the type as unknown if it is ambiguous"

> + *
> + * @param core_string
> + *   A string describing the intended cores to be parsed
> + * @return
> + *   int representing the core type
> + *   -1: error.

Suggest "negative error code on error". We should also list out the error
codes at the end, though I think right now -EINVAL is the only one we need.

> + *   0: coremask.
> + *   1: corelist.
> + *   2: unknown (ambiguous).

Move the #defines from the C file to the header, and use them here rather
than magic numbers.

> + */
> +__rte_experimental
> +int
> +rte_arg_parse_arg_type(const char *core_string);
> +
> +/**
> + * Convert a string describing either a corelist or coremask into an array of core ids.
> + *
> + * This function will fill the "cores" array up to "cores_len" with the core ids described
> + * in the "core_string". The string can either describe a corelist or a coremask, and
> + * will be parsed accordingly. The number of unique core ids in the string is then returned.
> + * For example:
> + * "1-4" is treated as a corelist and results in an array of [1,2,3,4] with 4 being returned
> + * "0xA1" is treated as a coremask and results in an array of [0,5,7] with 3 being returned
> + *
> + * In the case of an ambiguous string, the function will use the default_type parameter to
> + * decide.
> + *
> + * NOTE: if the length of the input array is insufficient to hold the number of core ids
> + * in "core_string" the input array is filled to capacity but the return value is the
> + * number of elements which would have been written to the array, had enough space been
> + * available. [This is similar to the behaviour of the snprintf function]. Because of
> + * this, the number of core values in the "core_string" may be determined by calling the
> + * function with a NULL array pointer and array length given as 0.
> + *
> + * @param core_string
> + *   A string describing the intended cores to be parsed.
> + * @param cores
> + *   An array where to store the core ids.
> + *   Array can be NULL if "cores_len" is 0.
> + * @param cores_len
> + *   The length of the "cores" array.
> + *   If the size is smaller than that needed to hold all cores from "core_string"
> + * @param default_type
> + *   How to treat ambiguous cases (e.g. '4' could be mask or list).
> + *   0: mask.
> + *   1: list.

Again, use the defines.

> + * @return
> + *   n: the number of unique cores present in "core_string".
> + *   -1 if the string was invalid.
> + *   NOTE: if n > "cores_len", then only "cores_len" elements in the "cores" array are valid.
> + */
> +__rte_experimental
> +int
> +rte_arg_parse_core_string(const char *core_string, uint16_t *cores, uint32_t cores_len,
> +		int default_type);
>  
>  #ifdef __cplusplus
>  }
> diff --git a/lib/arg_parser/version.map b/lib/arg_parser/version.map
> index b44d4b02b7..383b6bd0e9 100644
> --- a/lib/arg_parser/version.map
> +++ b/lib/arg_parser/version.map
> @@ -8,4 +8,6 @@ EXPERIMENTAL {
>  	# added in 24.03
>  	rte_arg_parse_corelist;
>  	rte_arg_parse_coremask;
> +	rte_arg_parse_arg_type;
> +	rte_arg_parse_core_string;


The version.map lists are kept alphabetical, so the new entries need to be
moved up.

>  };
> -- 
> 2.34.1
> 

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

* Re: [PATCH v3 0/8] add new command line argument parsing library
  2023-12-07 16:18 [PATCH v3 0/8] add new command line argument parsing library Euan Bourke
                   ` (7 preceding siblings ...)
  2023-12-07 16:18 ` [PATCH v3 8/8] examples/l3fwd-power: " Euan Bourke
@ 2023-12-07 17:34 ` Stephen Hemminger
  8 siblings, 0 replies; 15+ messages in thread
From: Stephen Hemminger @ 2023-12-07 17:34 UTC (permalink / raw)
  To: Euan Bourke; +Cc: dev

On Thu,  7 Dec 2023 16:18:10 +0000
Euan Bourke <euan.bourke@intel.com> wrote:

> A recent thread on the mailing list[1] discussed corelist and coremask
> parsing and the idea of a new library dedicated to command line parsing
> was mentioned[2]. This patchset adds the library, along with the new
> APIs, and edits the existing EAL, DLB2 driver and some example
> application functions to use these APIs, rather than each implementing
> their own copies.
> 
> The new APIs work similar to the existing functions in EAL, however
> instead of filling a core array like this:
> [1, -1, -1, 2, 3] (a non -1 refers to an 'active core' at that index)
> It fills it like this:
> [0, 3, 4] (with the value at each index being an 'active core').
> 
> The new APIs will also return the number of cores contained in the
> passed corelist/coremask, so in the above example, 3 would be returned.
> 
> New in the v3, is a heuristic parser which searches for key markers in
> the core string, returning a enum value based off this search to
> indicate if a parameter is likely a coremask or a corelist. This
> heuristic function is also wrapped in a parser function allowing
> apps to handle both coremasks and corelists simultaneously.
> 
> [1] https://mails.dpdk.org/archives/dev/2023-November/280957.html
> [2] https://mails.dpdk.org/archives/dev/2023-November/280966.html
> 

To avoid confusion with the other work to handle better args
in DPDK, maybe this should be call coreparse library?

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

* Re: [PATCH v3 1/8] arg_parser: new library for command line parsing
  2023-12-07 16:18 ` [PATCH v3 1/8] arg_parser: new library for command line parsing Euan Bourke
  2023-12-07 16:44   ` Bruce Richardson
@ 2023-12-07 19:32   ` Tyler Retzlaff
  1 sibling, 0 replies; 15+ messages in thread
From: Tyler Retzlaff @ 2023-12-07 19:32 UTC (permalink / raw)
  To: Euan Bourke; +Cc: dev, Thomas Monjalon, Bruce Richardson

On Thu, Dec 07, 2023 at 04:18:11PM +0000, Euan Bourke wrote:
> Add a new library to make it easier for eal and other libraries to parse
> command line arguments.
> 
> The first function in this library is one to parse a corelist string
> into an array of individual core ids. The function will then return the
> total number of cores described in the corelist.
> 
> Signed-off-by: Euan Bourke <euan.bourke@intel.com>
> ---
>  .mailmap                        |   1 +
>  MAINTAINERS                     |   4 ++
>  doc/api/doxy-api-index.md       |   3 +-
>  doc/api/doxy-api.conf.in        |   1 +
>  lib/arg_parser/arg_parser.c     | 108 ++++++++++++++++++++++++++++++++
>  lib/arg_parser/meson.build      |   7 +++
>  lib/arg_parser/rte_arg_parser.h |  66 +++++++++++++++++++
>  lib/arg_parser/version.map      |  10 +++
>  lib/meson.build                 |   2 +
>  9 files changed, 201 insertions(+), 1 deletion(-)
>  create mode 100644 lib/arg_parser/arg_parser.c
>  create mode 100644 lib/arg_parser/meson.build
>  create mode 100644 lib/arg_parser/rte_arg_parser.h
>  create mode 100644 lib/arg_parser/version.map
> 
> diff --git a/.mailmap b/.mailmap
> index ab0742a382..528bc68a30 100644
> --- a/.mailmap
> +++ b/.mailmap
> @@ -379,6 +379,7 @@ Eric Zhang <eric.zhang@windriver.com>
>  Erik Gabriel Carrillo <erik.g.carrillo@intel.com>
>  Erik Ziegenbalg <eziegenb@brocade.com>
>  Erlu Chen <erlu.chen@intel.com>
> +Euan Bourke <euan.bourke@intel.com>
>  Eugenio Pérez <eperezma@redhat.com>
>  Eugeny Parshutin <eugeny.parshutin@linux.intel.com>
>  Evan Swanson <evan.swanson@intel.com>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0d1c8126e3..68ef5ba14b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1756,6 +1756,10 @@ M: Nithin Dabilpuram <ndabilpuram@marvell.com>
>  M: Pavan Nikhilesh <pbhagavatula@marvell.com>
>  F: lib/node/
>  
> +Argument parsing
> +M: Bruce Richardson <bruce.richardson@intel.com>
> +F: lib/arg_parser/
> +
>  
>  Test Applications
>  -----------------
> diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> index a6a768bd7c..f711010140 100644
> --- a/doc/api/doxy-api-index.md
> +++ b/doc/api/doxy-api-index.md
> @@ -221,7 +221,8 @@ The public API headers are grouped by topics:
>    [config file](@ref rte_cfgfile.h),
>    [key/value args](@ref rte_kvargs.h),
>    [string](@ref rte_string_fns.h),
> -  [thread](@ref rte_thread.h)
> +  [thread](@ref rte_thread.h),
> +  [argument parsing](@ref rte_arg_parser.h)
>  
>  - **debug**:
>    [jobstats](@ref rte_jobstats.h),
> diff --git a/doc/api/doxy-api.conf.in b/doc/api/doxy-api.conf.in
> index e94c9e4e46..05718ba6ed 100644
> --- a/doc/api/doxy-api.conf.in
> +++ b/doc/api/doxy-api.conf.in
> @@ -28,6 +28,7 @@ INPUT                   = @TOPDIR@/doc/api/doxy-api-index.md \
>                            @TOPDIR@/lib/eal/include \
>                            @TOPDIR@/lib/eal/include/generic \
>                            @TOPDIR@/lib/acl \
> +                          @TOPDIR@/lib/arg_parser \
>                            @TOPDIR@/lib/bbdev \
>                            @TOPDIR@/lib/bitratestats \
>                            @TOPDIR@/lib/bpf \
> diff --git a/lib/arg_parser/arg_parser.c b/lib/arg_parser/arg_parser.c
> new file mode 100644
> index 0000000000..240f63d8e1
> --- /dev/null
> +++ b/lib/arg_parser/arg_parser.c
> @@ -0,0 +1,108 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(c) 2023 Intel Corporation
> + */
> +
> +#include "errno.h"
> +#include "stdlib.h"
> +#include "ctype.h"
> +#include "string.h"
> +#include "stdbool.h"

should be <> not "" quotes for system or standard library headers includes.

> +
> +#include <rte_arg_parser.h>
> +#include <rte_common.h>
> +
> +
> +struct core_bits {
> +	uint8_t bits[(UINT16_MAX + 1) / CHAR_BIT];
> +	uint16_t max_bit_set;
> +	uint16_t min_bit_set;
> +	uint32_t total_bits_set;
> +};
> +
> +static inline bool
> +get_core_bit(struct core_bits *mask, uint16_t idx)

nit: use size_t typed parameters for things that are used as an offset /
subscript value. applies to all instances in the series.


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

* Re: [PATCH v3 8/8] examples/l3fwd-power: update to call arg parser API
  2023-12-07 16:18 ` [PATCH v3 8/8] examples/l3fwd-power: " Euan Bourke
@ 2023-12-11 12:01   ` Hunt, David
  2023-12-11 16:50   ` Tummala, Sivaprasad
  1 sibling, 0 replies; 15+ messages in thread
From: Hunt, David @ 2023-12-11 12:01 UTC (permalink / raw)
  To: Euan Bourke, dev; +Cc: Anatoly Burakov, Sivaprasad Tummala

Hi Euan,

On 07/12/2023 16:18, Euan Bourke wrote:
> Update to the l3fwd-power example application to call the arg parser
> library for its 'combined core string parser' instead of implementing its
> own corelist parser. The default_type passed into the function call is
> a corelist.
>
> Signed-off-by: Euan Bourke <euan.bourke@intel.com>
> ---
>   examples/l3fwd-power/perf_core.c | 51 +++++---------------------------
>   1 file changed, 8 insertions(+), 43 deletions(-)
>
> diff --git a/examples/l3fwd-power/perf_core.c b/examples/l3fwd-power/perf_core.c
> index 41ef6d0c9a..f8511e30b3 100644
> --- a/examples/l3fwd-power/perf_core.c
> +++ b/examples/l3fwd-power/perf_core.c
> @@ -12,6 +12,7 @@
>   #include <rte_lcore.h>
>   #include <rte_power.h>
>   #include <rte_string_fns.h>
> +#include <rte_arg_parser.h>
>   
>   #include "perf_core.h"
>   #include "main.h"
> @@ -177,56 +178,20 @@ parse_perf_config(const char *q_arg)
>   int
>   parse_perf_core_list(const char *corelist)
>   {
> -	int i, idx = 0;
> -	unsigned int count = 0;
> -	char *end = NULL;
> -	int min, max;
> +	int count;
> +	uint16_t cores[RTE_MAX_LCORE];
>   
>   	if (corelist == NULL) {
>   		printf("invalid core list\n");
>   		return -1;
>   	}
>   
> +	count = rte_arg_parse_core_string(corelist, cores, RTE_DIM(cores), 1);
>   
> -	/* Remove all blank characters ahead and after */
> -	while (isblank(*corelist))
> -		corelist++;
> -	i = strlen(corelist);
> -	while ((i > 0) && isblank(corelist[i - 1]))
> -		i--;
> +	for (int i = 0; i < count; i++)


nit: you've used int here, but below you use uint16_t for a for loop. If 
you're re-spinning, it might be worth making consistent. But no biggie.

--snip--

> @@ -234,7 +199,7 @@ parse_perf_core_list(const char *corelist)
>   	nb_hp_lcores = count;
>   
>   	printf("Configured %d high performance cores\n", nb_hp_lcores);
> -	for (i = 0; i < nb_hp_lcores; i++)
> +	for (uint16_t i = 0; i < nb_hp_lcores; i++)
>   		printf("\tHigh performance core %d %d\n",
>   				i, hp_lcores[i]);
>   


I've also tested this with a 16-core incantation of l3fwd-power with 
various combinations of cores, seems to work well.

Acked-by: David Hunt <david.hunt@intel.com>






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

* RE: [PATCH v3 8/8] examples/l3fwd-power: update to call arg parser API
  2023-12-07 16:18 ` [PATCH v3 8/8] examples/l3fwd-power: " Euan Bourke
  2023-12-11 12:01   ` Hunt, David
@ 2023-12-11 16:50   ` Tummala, Sivaprasad
  1 sibling, 0 replies; 15+ messages in thread
From: Tummala, Sivaprasad @ 2023-12-11 16:50 UTC (permalink / raw)
  To: Euan Bourke, dev; +Cc: Anatoly Burakov, David Hunt

[AMD Official Use Only - General]

Hi Euan,

> -----Original Message-----
> From: Euan Bourke <euan.bourke@intel.com>
> Sent: Thursday, December 7, 2023 9:48 PM
> To: dev@dpdk.org
> Cc: Euan Bourke <euan.bourke@intel.com>; Anatoly Burakov
> <anatoly.burakov@intel.com>; David Hunt <david.hunt@intel.com>; Tummala,
> Sivaprasad <Sivaprasad.Tummala@amd.com>
> Subject: [PATCH v3 8/8] examples/l3fwd-power: update to call arg parser API
>
> Caution: This message originated from an External Source. Use proper caution
> when opening attachments, clicking links, or responding.
>
>
> Update to the l3fwd-power example application to call the arg parser library for its
> 'combined core string parser' instead of implementing its own corelist parser. The
> default_type passed into the function call is a corelist.
>
> Signed-off-by: Euan Bourke <euan.bourke@intel.com>
> ---
>  examples/l3fwd-power/perf_core.c | 51 +++++---------------------------
>  1 file changed, 8 insertions(+), 43 deletions(-)
>
> diff --git a/examples/l3fwd-power/perf_core.c b/examples/l3fwd-power/perf_core.c
> index 41ef6d0c9a..f8511e30b3 100644
> --- a/examples/l3fwd-power/perf_core.c
> +++ b/examples/l3fwd-power/perf_core.c
> @@ -12,6 +12,7 @@
>  #include <rte_lcore.h>
>  #include <rte_power.h>
>  #include <rte_string_fns.h>
> +#include <rte_arg_parser.h>
>
>  #include "perf_core.h"
>  #include "main.h"
> @@ -177,56 +178,20 @@ parse_perf_config(const char *q_arg)  int
> parse_perf_core_list(const char *corelist)  {
> -       int i, idx = 0;
> -       unsigned int count = 0;
> -       char *end = NULL;
> -       int min, max;
> +       int count;
> +       uint16_t cores[RTE_MAX_LCORE];
>
>         if (corelist == NULL) {
>                 printf("invalid core list\n");
>                 return -1;
>         }
>
> +       count = rte_arg_parse_core_string(corelist, cores,
> + RTE_DIM(cores), 1);
Can you replace the magic number with "RTE_ARG_PARSE_TYPE_CORELIST" as default parse type.

>
> -       /* Remove all blank characters ahead and after */
> -       while (isblank(*corelist))
> -               corelist++;
> -       i = strlen(corelist);
> -       while ((i > 0) && isblank(corelist[i - 1]))
> -               i--;
> +       for (int i = 0; i < count; i++)
> +               hp_lcores[i] = cores[i];
>
> -       /* Get list of cores */
> -       min = RTE_MAX_LCORE;
> -       do {
> -               while (isblank(*corelist))
> -                       corelist++;
> -               if (*corelist == '\0')
> -                       return -1;
> -               errno = 0;
> -               idx = strtoul(corelist, &end, 10);
> -               if (errno || end == NULL)
> -                       return -1;
> -               while (isblank(*end))
> -                       end++;
> -               if (*end == '-') {
> -                       min = idx;
> -               } else if ((*end == ',') || (*end == '\0')) {
> -                       max = idx;
> -                       if (min == RTE_MAX_LCORE)
> -                               min = idx;
> -                       for (idx = min; idx <= max; idx++) {
> -                               hp_lcores[count] = idx;
> -                               count++;
> -                       }
> -                       min = RTE_MAX_LCORE;
> -               } else {
> -                       printf("invalid core list\n");
> -                       return -1;
> -               }
> -               corelist = end + 1;
> -       } while (*end != '\0');
> -
> -       if (count == 0) {
> +       if (count == 0 || count == -1) {
>                 printf("invalid core list\n");
>                 return -1;
>         }
> @@ -234,7 +199,7 @@ parse_perf_core_list(const char *corelist)
>         nb_hp_lcores = count;
>
>         printf("Configured %d high performance cores\n", nb_hp_lcores);
> -       for (i = 0; i < nb_hp_lcores; i++)
> +       for (uint16_t i = 0; i < nb_hp_lcores; i++)
>                 printf("\tHigh performance core %d %d\n",
>                                 i, hp_lcores[i]);
>
> --
> 2.34.1


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

end of thread, other threads:[~2023-12-11 16:50 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-07 16:18 [PATCH v3 0/8] add new command line argument parsing library Euan Bourke
2023-12-07 16:18 ` [PATCH v3 1/8] arg_parser: new library for command line parsing Euan Bourke
2023-12-07 16:44   ` Bruce Richardson
2023-12-07 19:32   ` Tyler Retzlaff
2023-12-07 16:18 ` [PATCH v3 2/8] arg_parser: add new coremask parsing API Euan Bourke
2023-12-07 16:18 ` [PATCH v3 3/8] eal: add support for new arg parsing library Euan Bourke
2023-12-07 16:18 ` [PATCH v3 4/8] eal: update to service core related parsers Euan Bourke
2023-12-07 16:18 ` [PATCH v3 5/8] event/dlb2: add new arg parsing library API support Euan Bourke
2023-12-07 16:18 ` [PATCH v3 6/8] arg_parser: added common core string and heuristic parsers Euan Bourke
2023-12-07 16:58   ` Bruce Richardson
2023-12-07 16:18 ` [PATCH v3 7/8] examples/eventdev_pipeline: update to call arg parser API Euan Bourke
2023-12-07 16:18 ` [PATCH v3 8/8] examples/l3fwd-power: " Euan Bourke
2023-12-11 12:01   ` Hunt, David
2023-12-11 16:50   ` Tummala, Sivaprasad
2023-12-07 17:34 ` [PATCH v3 0/8] add new command line argument parsing library Stephen Hemminger

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).