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

