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 <[email protected]>
---
 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@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

Reply via email to