[PATCH] argparse: support range step for corelist parsing

Chengwen Feng fengchengwen at huawei.com
Fri Oct 16 11:30:01 CEST 2026


Introduce a stride/step syntax ('-l start-end:step') to the EAL
corelist parser implemented within the argparse library.

On modern multi-core platforms, especially ARM architectures (such
as Kunpeng) logical sibling cores are frequently laid out contiguously
(e.g., physical core 0 maps to logical cores 0 and 1). When deploying
high-performance DPDK data planes, pinning worker threads to adjacent
sibling cores introduces Heavy L1/L2 cache contention and drastically
degrades overall throughput.

Previously, users had to explicitly expand core lists via commas
(e.g., "-l 0,2,4,6...126") in orchestration scripts to skip
hyperthreads, which is error-prone and hurts maintainability.

This patch introduces the range step, so we could use "-l 0-126:2" in
the same case.

Signed-off-by: Chengwen Feng <fengchengwen at huawei.com>
---
 app/test/test_argparse.c               | 96 ++++++++++++++++++++++++++
 doc/guides/prog_guide/argparse_lib.rst |  2 +
 lib/argparse/rte_argparse.c            | 53 ++++++++++----
 3 files changed, 139 insertions(+), 12 deletions(-)

diff --git a/app/test/test_argparse.c b/app/test/test_argparse.c
index fdc6dd113c..a87dc461b3 100644
--- a/app/test/test_argparse.c
+++ b/app/test/test_argparse.c
@@ -822,8 +822,11 @@ test_argparse_parse_type_corelist(void)
 	char *corelist_valid_single = test_strdup("5");
 	char *corelist_valid_multiple = test_strdup("0,1,5");
 	char *corelist_valid_range = test_strdup("1-5");
+	char *corelist_valid_range_step = test_strdup("1-5:2");
 	char *corelist_valid_mixed = test_strdup("0,1,5-10,12-16,18,20");
+	char *corelist_valid_mixed_step = test_strdup("0,1,5-10:2,12-16:3,18,20");
 	char *corelist_valid_reverse_range = test_strdup("10-5");
+	char *corelist_valid_reverse_range_step = test_strdup("10-5:2");
 	char *corelist_valid_initial_spaces = test_strdup("   1,2,5-7");
 	char *corelist_valid_empty = test_strdup("");
 	char *corelist_invalid_spaces = test_strdup(" 1 , 2 , 5-7 ");
@@ -831,6 +834,12 @@ test_argparse_parse_type_corelist(void)
 	char *corelist_invalid_range_incomplete = test_strdup("1-");
 	char *corelist_invalid_range_double_dash = test_strdup("1--5");
 	char *corelist_invalid_range_double_range = test_strdup("1-3-5");
+	char *corelist_invalid_range_step_incomplete1 = test_strdup("1:");
+	char *corelist_invalid_range_step_incomplete2 = test_strdup("1:2");
+	char *corelist_invalid_range_step_incomplete3 = test_strdup("1-5:");
+	char *corelist_invalid_range_step_double = test_strdup("1-5::");
+	char *corelist_invalid_range_step_negative = test_strdup("1-5:-2:");
+	char *corelist_invalid_range_step_more = test_strdup("1-5:2:");
 	char *corelist_invalid_special_chars = test_strdup("1,2 at 3");
 	char *corelist_invalid_comma_only = test_strdup(",");
 	char *corelist_invalid_out_of_range = test_strdup("70000");
@@ -867,6 +876,18 @@ test_argparse_parse_type_corelist(void)
 	TEST_ASSERT(!CPU_ISSET(0, &val_cpuset), "Core 0 should not be set in range 1-5!");
 	TEST_ASSERT(!CPU_ISSET(6, &val_cpuset), "Core 6 should not be set in range 1-5!");
 
+	/* test valid range step */
+	CPU_ZERO(&val_cpuset);
+	ret = rte_argparse_parse_type(corelist_valid_range_step,
+			RTE_ARGPARSE_VALUE_TYPE_CORELIST, &val_cpuset);
+	TEST_ASSERT(ret == 0, "Argparse parse type for corelist (range step) failed!");
+	for (int i = 1; i <= 5; i += 2)
+		TEST_ASSERT(CPU_ISSET(i, &val_cpuset), "Core %d should be set in range 1-5:2!", i);
+	for (int i = 2; i <= 5; i += 2)
+		TEST_ASSERT(!CPU_ISSET(i, &val_cpuset), "Core %d should not be set in range 1-5:2!", i);
+	TEST_ASSERT(!CPU_ISSET(0, &val_cpuset), "Core 0 should not be set in range 1-5:2!");
+	TEST_ASSERT(!CPU_ISSET(6, &val_cpuset), "Core 6 should not be set in range 1-5:2!");
+
 	/* test valid mixed corelist */
 	CPU_ZERO(&val_cpuset);
 	ret = rte_argparse_parse_type(corelist_valid_mixed,
@@ -886,6 +907,31 @@ test_argparse_parse_type_corelist(void)
 	TEST_ASSERT(!CPU_ISSET(17, &val_cpuset), "Core 17 should not be set in mixed corelist!");
 	TEST_ASSERT(!CPU_ISSET(19, &val_cpuset), "Core 19 should not be set in mixed corelist!");
 
+	/* test valid mixed step corelist */
+	CPU_ZERO(&val_cpuset);
+	ret = rte_argparse_parse_type(corelist_valid_mixed_step,
+			RTE_ARGPARSE_VALUE_TYPE_CORELIST, &val_cpuset);
+	TEST_ASSERT(ret == 0, "Argparse parse type for corelist (mixed step) failed!");
+	TEST_ASSERT(CPU_ISSET(0, &val_cpuset), "Core 0 should be set in mixed step corelist!");
+	TEST_ASSERT(CPU_ISSET(1, &val_cpuset), "Core 1 should be set in mixed step corelist!");
+	for (int i = 5; i <= 10; i += 2)
+		TEST_ASSERT(CPU_ISSET(i, &val_cpuset), "Core %d should be set in range 5-10:2!", i);
+	for (int i = 6; i <= 10; i += 2)
+		TEST_ASSERT(!CPU_ISSET(i, &val_cpuset), "Core %d should not be set in range 5-10:2!", i);
+	for (int i = 12; i <= 16; i += 3)
+		TEST_ASSERT(CPU_ISSET(i, &val_cpuset), "Core %d should be set in range 12-16:3!", i);
+	for (int i = 13; i <= 16; i += 3)
+		TEST_ASSERT(!CPU_ISSET(i, &val_cpuset), "Core %d should not be set in range 12-16:3!", i);
+	for (int i = 14; i <= 16; i += 3)
+		TEST_ASSERT(!CPU_ISSET(i, &val_cpuset), "Core %d should not be set in range 12-16:3!", i);
+	TEST_ASSERT(CPU_ISSET(18, &val_cpuset), "Core 18 should be set in mixed step corelist!");
+	TEST_ASSERT(CPU_ISSET(20, &val_cpuset), "Core 20 should be set in mixed step corelist!");
+	TEST_ASSERT(!CPU_ISSET(2, &val_cpuset), "Core 2 should not be set in mixed step corelist!");
+	TEST_ASSERT(!CPU_ISSET(11, &val_cpuset), "Core 11 should not be set in mixed step corelist!");
+	TEST_ASSERT(!CPU_ISSET(17, &val_cpuset), "Core 17 should not be set in mixed step corelist!");
+	TEST_ASSERT(!CPU_ISSET(19, &val_cpuset), "Core 19 should not be set in mixed step corelist!");
+
+
 	/* test valid reverse range (10-5 should be interpreted as 5-10) */
 	CPU_ZERO(&val_cpuset);
 	ret = rte_argparse_parse_type(corelist_valid_reverse_range,
@@ -897,6 +943,20 @@ test_argparse_parse_type_corelist(void)
 	TEST_ASSERT(!CPU_ISSET(4, &val_cpuset), "Core 4 should not be set in reverse range 10-5!");
 	TEST_ASSERT(!CPU_ISSET(11, &val_cpuset), "Core 11 should not be set in reverse range 10-5!");
 
+	/* test valid reverse range step (10-5:2 should be interpreted as 5-10:2) */
+	CPU_ZERO(&val_cpuset);
+	ret = rte_argparse_parse_type(corelist_valid_reverse_range_step,
+			RTE_ARGPARSE_VALUE_TYPE_CORELIST, &val_cpuset);
+	TEST_ASSERT(ret == 0, "Argparse parse type for corelist (reverse range) failed!");
+	for (int i = 5; i <= 10; i += 2)
+		TEST_ASSERT(CPU_ISSET(i, &val_cpuset),
+				"Core %d should be set in reverse range 10-5:2!", i);
+	for (int i = 6; i <= 10; i += 2)
+		TEST_ASSERT(!CPU_ISSET(i, &val_cpuset),
+				"Core %d should not be set in reverse range 10-5:2!", i);
+	TEST_ASSERT(!CPU_ISSET(4, &val_cpuset), "Core 4 should not be set in reverse range 10-5!");
+	TEST_ASSERT(!CPU_ISSET(11, &val_cpuset), "Core 11 should not be set in reverse range 10-5!");
+
 	/* test valid corelist with initial spaces only */
 	CPU_ZERO(&val_cpuset);
 	ret = rte_argparse_parse_type(corelist_valid_initial_spaces,
@@ -948,6 +1008,42 @@ test_argparse_parse_type_corelist(void)
 			RTE_ARGPARSE_VALUE_TYPE_CORELIST, &val_cpuset);
 	TEST_ASSERT(ret != 0, "Argparse parse type for corelist (double range) should have failed!");
 
+	/* test invalid corelist with step complete1 */
+	CPU_ZERO(&val_cpuset);
+	ret = rte_argparse_parse_type(corelist_invalid_range_step_incomplete1,
+			RTE_ARGPARSE_VALUE_TYPE_CORELIST, &val_cpuset);
+	TEST_ASSERT(ret != 0, "Argparse parse type for corelist (step incomplete1) should have failed!");
+
+	/* test invalid corelist with step incomplete2 */
+	CPU_ZERO(&val_cpuset);
+	ret = rte_argparse_parse_type(corelist_invalid_range_step_incomplete2,
+			RTE_ARGPARSE_VALUE_TYPE_CORELIST, &val_cpuset);
+	TEST_ASSERT(ret != 0, "Argparse parse type for corelist (step incomplete2) should have failed!");
+
+	/* test invalid corelist with step incomplete3 */
+	CPU_ZERO(&val_cpuset);
+	ret = rte_argparse_parse_type(corelist_invalid_range_step_incomplete3,
+			RTE_ARGPARSE_VALUE_TYPE_CORELIST, &val_cpuset);
+	TEST_ASSERT(ret != 0, "Argparse parse type for corelist (step incomplete3) should have failed!");
+
+	/* test invalid corelist with step double */
+	CPU_ZERO(&val_cpuset);
+	ret = rte_argparse_parse_type(corelist_invalid_range_step_double,
+			RTE_ARGPARSE_VALUE_TYPE_CORELIST, &val_cpuset);
+	TEST_ASSERT(ret != 0, "Argparse parse type for corelist (step double) should have failed!");
+
+	/* test invalid corelist with step negative */
+	CPU_ZERO(&val_cpuset);
+	ret = rte_argparse_parse_type(corelist_invalid_range_step_negative,
+			RTE_ARGPARSE_VALUE_TYPE_CORELIST, &val_cpuset);
+	TEST_ASSERT(ret != 0, "Argparse parse type for corelist (step negative) should have failed!");
+
+	/* test invalid corelist with step more */
+	CPU_ZERO(&val_cpuset);
+	ret = rte_argparse_parse_type(corelist_invalid_range_step_more,
+			RTE_ARGPARSE_VALUE_TYPE_CORELIST, &val_cpuset);
+	TEST_ASSERT(ret != 0, "Argparse parse type for corelist (step more) should have failed!");
+
 	/* test invalid corelist with special characters */
 	CPU_ZERO(&val_cpuset);
 	ret = rte_argparse_parse_type(corelist_invalid_special_chars,
diff --git a/doc/guides/prog_guide/argparse_lib.rst b/doc/guides/prog_guide/argparse_lib.rst
index b0907cfc07..3d01406599 100644
--- a/doc/guides/prog_guide/argparse_lib.rst
+++ b/doc/guides/prog_guide/argparse_lib.rst
@@ -271,8 +271,10 @@ The corelist parsing supports the following input formats:
 - **Single core**: ``--cores 5`` (sets core 5)
 - **Multiple cores**: ``--cores 1,2,5`` (sets cores 1, 2, and 5)
 - **Core ranges**: ``--cores 1-5`` (sets cores 1, 2, 3, 4, and 5)
+- **Core ranges with step**: ``--cores 1-5:2`` (sets cores 1, 3, and 5)
 - **Mixed format**: ``--cores 0,2-4,7`` (sets cores 0, 2, 3, 4, and 7)
 - **Reverse ranges**: ``--cores 5-1`` (equivalent to 1-5, sets cores 1, 2, 3, 4, and 5)
+- **Reverse ranges with step**: ``--cores 5-1:2`` (equivalent to 1-5:2, sets cores 1, 3, and 5)
 - **Empty corelist**: ``--cores ""`` (sets no cores)
 
 The parsed result is stored in an ``rte_cpuset_t`` structure that can be used
diff --git a/lib/argparse/rte_argparse.c b/lib/argparse/rte_argparse.c
index 88c3e7740c..f1e1fb9d20 100644
--- a/lib/argparse/rte_argparse.c
+++ b/lib/argparse/rte_argparse.c
@@ -573,6 +573,9 @@ parse_arg_corelist(const struct rte_argparse_arg *arg, const char *value)
 {
 	rte_cpuset_t *cpuset = arg->val_saver;
 	const char *last = value;
+	bool is_step = false;
+	int32_t step = 1;
+	int32_t max = -1;
 	int min = -1;
 
 	if (value == NULL) {
@@ -584,7 +587,6 @@ parse_arg_corelist(const struct rte_argparse_arg *arg, const char *value)
 	while (*last != '\0') {
 		char *end;
 		int64_t idx;
-		int32_t max;
 
 		while (isblank(*value))
 			value++;
@@ -603,6 +605,14 @@ parse_arg_corelist(const struct rte_argparse_arg *arg, const char *value)
 				arg->name_long);
 			return -EINVAL;
 		}
+		if (is_step) {
+			if (idx == 0) {
+				ARGPARSE_LOG(ERR, "argument %s step value must be positive!",
+					arg->name_long);
+				return -EINVAL;
+			}
+			step = idx;
+		}
 
 		if (*end == '-') {
 			if (min != -1) { /* can't have '-' within a range */
@@ -612,22 +622,41 @@ parse_arg_corelist(const struct rte_argparse_arg *arg, const char *value)
 			}
 			min = idx; /* start of range, move to next loop stage */
 		} else if (*end == ',' || *end == '\0') {
-			/* single value followed by comma or end (min is set only by '-') */
-			if (min == -1) {
-				min = max = idx;
-			} else if (min > idx) {
-				/* we have range from high to low */
+			if (!is_step) {
+				/* single value followed by comma or end (min is set only by '-') */
+				if (min == -1) {
+					min = max = idx;
+				} else if (min > idx) {
+					/* we have range from high to low */
+					max = min;
+					min = idx;
+				} else {
+					/* range from low to high */
+					max = idx;
+				}
+			}
+
+			for (; min <= max; min += step)
+				CPU_SET(min, cpuset);
+
+			/* no longer in a range */
+			min = -1;
+			max = -1;
+			step = 1;
+			is_step = false;
+		} else if (*end == ':') {
+			if (min == -1 || is_step) {
+				ARGPARSE_LOG(ERR, "argument %s step can only be used after a range (start-end:step)!",
+					arg->name_long);
+				return -EINVAL;
+			}
+			if (min > idx) {
 				max = min;
 				min = idx;
 			} else {
-				/* range from low to high */
 				max = idx;
 			}
-
-			for (; min <= max; min++)
-				CPU_SET(min, cpuset);
-
-			min = -1; /* no longer in a range */
+			is_step = true;
 		} else {
 			/* end is an unexpected character, return error */
 			ARGPARSE_LOG(ERR, "argument %s has an unexpected character!",
-- 
2.17.1



More information about the dev mailing list