[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