From 5bb90e812efe402472d44e0f453951338c06ffba Mon Sep 17 00:00:00 2001 From: Oleksij Rempel Date: Mon, 27 Feb 2023 09:50:01 +0100 Subject: [PATCH] enh: Add support for SO_PRIORITY socket option. This functionality is needed to test multi queue support of Ethernet drivers. Co-developed-by: Oleksij Rempel Signed-off-by: Oleksij Rempel Co-developed-by: Marc Kleine-Budde Signed-off-by: Marc Kleine-Budde --- configure.ac | 12 ++++++++++++ docs/invoking.rst | 8 ++++++++ src/iperf.h | 3 +++ src/iperf_api.c | 47 +++++++++++++++++++++++++++++++++++++++++++++- src/iperf_api.h | 3 +++ src/iperf_locale.c | 3 +++ 6 files changed, 75 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 1b3c413f1..67ffffc87 100644 --- a/configure.ac +++ b/configure.ac @@ -242,6 +242,18 @@ if test "x$iperf3_cv_header_so_bindtodevice" = "xyes"; then AC_DEFINE([HAVE_SO_BINDTODEVICE], [1], [Have SO_BINDTODEVICE sockopt.]) fi +# Check for SO_PRIORITY sockopt (believed to be Linux only) +AC_CACHE_CHECK([SO_PRIORITY socket option], +[iperf3_cv_header_so_priority], +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#include ]], + [[int foo = SO_PRIORITY;]])], + iperf3_cv_header_so_priority=yes, + iperf3_cv_header_so_priority=no)) +if test "x$iperf3_cv_header_so_priority" = "xyes"; then + AC_DEFINE([HAVE_SO_PRIORITY], [1], [Have SO_PRIORITY sockopt.]) +fi + # Check for IP_MTU_DISCOVER (mostly on Linux) AC_CACHE_CHECK([IP_MTU_DISCOVER socket option], [iperf3_cv_header_ip_mtu_discover], diff --git a/docs/invoking.rst b/docs/invoking.rst index 5b09463fa..1b0717e8f 100644 --- a/docs/invoking.rst +++ b/docs/invoking.rst @@ -375,6 +375,14 @@ the executable. set the IP type of service. The usual prefixes for octal and hex can be used, i.e. 52, 064 and 0x34 all specify the same value. + --sock-prio n + Set the protocol-defined priority for all packets to be sent + on this socket. Linux uses this value to order the networking + queues: packets with a higher priority may be processed first + depending on the selected device queueing discipline. Setting + a priority outside the range 0 to 6 requires the CAP_NET_ADMIN + capability + --dscp dscp set the IP DSCP bits. Both numeric and symbolic values are accepted. Numeric values can be specified in decimal, octal and diff --git a/src/iperf.h b/src/iperf.h index c3ce33383..3b21bcf9c 100644 --- a/src/iperf.h +++ b/src/iperf.h @@ -151,6 +151,9 @@ struct iperf_settings int mss; /* for TCP MSS */ int ttl; /* IP TTL option */ int tos; /* type of service bit */ +#if defined(HAVE_SO_PRIORITY) + int sock_prio; /* protocol-defined priority */ +#endif // HAVE_SO_PRIORITY int flowlabel; /* IPv6 flow label */ iperf_size_t bytes; /* number of bytes to send */ iperf_size_t blocks; /* number of blocks (packets) to send */ diff --git a/src/iperf_api.c b/src/iperf_api.c index 855c1c20d..c83d66d03 100644 --- a/src/iperf_api.c +++ b/src/iperf_api.c @@ -881,7 +881,13 @@ void iperf_on_test_start(struct iperf_test *test) { if (test->json_output) { - cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d omit: %d duration: %d bytes: %d blocks: %d reverse: %d tos: %d target_bitrate: %d bidir: %d fqrate: %d", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->omit, (int64_t) test->duration, (int64_t) test->settings->bytes, (int64_t) test->settings->blocks, test->reverse?(int64_t)1:(int64_t)0, (int64_t) test->settings->tos, (int64_t) test->settings->rate, (int64_t) test->bidirectional, (uint64_t) test->settings->fqrate)); + cJSON_AddItemToObject(test->json_start, "test_start", iperf_json_printf("protocol: %s num_streams: %d blksize: %d omit: %d duration: %d bytes: %d blocks: %d reverse: %d tos: %d target_bitrate: %d bidir: %d fqrate: %d sock_prio: %d", test->protocol->name, (int64_t) test->num_streams, (int64_t) test->settings->blksize, (int64_t) test->omit, (int64_t) test->duration, (int64_t) test->settings->bytes, (int64_t) test->settings->blocks, test->reverse?(int64_t)1:(int64_t)0, (int64_t) test->settings->tos, (int64_t) test->settings->rate, (int64_t) test->bidirectional, (uint64_t) test->settings->fqrate, +#if defined(HAVE_SO_PRIORITY) + (int64_t) test->settings->sock_prio +#else /* HAVE_SO_PRIORITY */ + 0 +#endif + )); } else { if (test->verbose) { if (test->settings->bytes) @@ -1083,6 +1089,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) {"version4", no_argument, NULL, '4'}, {"version6", no_argument, NULL, '6'}, {"tos", required_argument, NULL, 'S'}, +#if defined(HAVE_SO_PRIORITY) + {"sock-prio", required_argument, NULL, OPT_SOCK_PRIO}, +#endif /* HAVE_SO_PRIORITY */ {"dscp", required_argument, NULL, OPT_DSCP}, {"extra-data", required_argument, NULL, OPT_EXTRA_DATA}, #if defined(HAVE_FLOWLABEL) @@ -1404,6 +1413,21 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv) } client_flag = 1; break; + case OPT_SOCK_PRIO: +#if defined(HAVE_SO_PRIORITY) + test->settings->sock_prio = strtol(optarg, &endptr, 0); + if (endptr == optarg || + test->settings->sock_prio < 0 || + test->settings->sock_prio > 15) { + i_errno = IEBADSOPRIO; + return -1; + } + client_flag = 1; +#else /* HAVE_SO_PRIORITY */ + i_errno = IEUNIMP; + return -1; +#endif /* HAVE_SO_PRIORITY */ + break; case OPT_DSCP: test->settings->tos = parse_qos(optarg); if(test->settings->tos < 0) { @@ -2210,6 +2234,10 @@ send_parameters(struct iperf_test *test) cJSON_AddNumberToObject(j, "burst", test->settings->burst); if (test->settings->tos) cJSON_AddNumberToObject(j, "TOS", test->settings->tos); +#if defined(HAVE_SO_PRIORITY) + if (test->settings->sock_prio) + cJSON_AddNumberToObject(j, "SOCK_PRIO", test->settings->sock_prio); +#endif /* HAVE_SO_PRIORITY */ if (test->settings->flowlabel) cJSON_AddNumberToObject(j, "flowlabel", test->settings->flowlabel); if (test->title) @@ -2326,6 +2354,10 @@ get_parameters(struct iperf_test *test) test->settings->burst = j_p->valueint; if ((j_p = cJSON_GetObjectItem(j, "TOS")) != NULL) test->settings->tos = j_p->valueint; +#if defined(HAVE_SO_PRIORITY) + if ((j_p = cJSON_GetObjectItem(j, "SOCK_PRIO")) != NULL) + test->settings->sock_prio = j_p->valueint; +#endif /* HAVE_SO_PRIORITY */ if ((j_p = cJSON_GetObjectItem(j, "flowlabel")) != NULL) test->settings->flowlabel = j_p->valueint; if ((j_p = cJSON_GetObjectItem(j, "title")) != NULL) @@ -3188,6 +3220,9 @@ iperf_reset_test(struct iperf_test *test) test->settings->burst = 0; test->settings->mss = 0; test->settings->tos = 0; +#if defined(HAVE_SO_PRIORITY) + test->settings->sock_prio = 0; +#endif /* HAVE_SO_PRIORITY */ test->settings->dont_fragment = 0; test->zerocopy = 0; @@ -4438,6 +4473,16 @@ iperf_common_sockopts(struct iperf_test *test, int s) } } } + +#if defined(HAVE_SO_PRIORITY) + if ((opt = test->settings->sock_prio)) { + if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, &opt, sizeof(opt)) < 0) { + i_errno = IESETSOPRIO; + return -1; + } + } +#endif /* HAVE_SO_PRIORITY */ + return 0; } diff --git a/src/iperf_api.h b/src/iperf_api.h index 093ea9700..fda72bc2e 100644 --- a/src/iperf_api.h +++ b/src/iperf_api.h @@ -90,6 +90,7 @@ typedef uint64_t iperf_size_t; #define OPT_DONT_FRAGMENT 26 #define OPT_RCV_TIMEOUT 27 #define OPT_SND_TIMEOUT 28 +#define OPT_SOCK_PRIO 29 /* states */ #define TEST_START 1 @@ -406,6 +407,7 @@ enum { IERVRSONLYRCVTIMEOUT = 32, // Client receive timeout is valid only in reverse mode IESNDTIMEOUT = 33, // Illegal message send timeout IEUDPFILETRANSFER = 34, // Cannot transfer file using UDP + IEBADSOPRIO = 35, // Bad socket priority value /* Test errors */ IENEWTEST = 100, // Unable to create a new test (check perror) IEINITTEST = 101, // Test initialization failed (check perror) @@ -456,6 +458,7 @@ enum { IEBINDDEVNOSUPPORT = 146, // `ip%%dev` is not supported as system does not support bind to device IEHOSTDEV = 147, // host device name (ip%%) is supported (and required) only for IPv6 link-local address IESETUSERTIMEOUT = 148, // Unable to set TCP USER_TIMEOUT (check perror) + IESETSOPRIO = 149, // Unable to set socket priority (check perror) /* Stream errors */ IECREATESTREAM = 200, // Unable to create a new stream (check herror/perror) IEINITSTREAM = 201, // Unable to initialize stream (check herror/perror) diff --git a/src/iperf_locale.c b/src/iperf_locale.c index 838086e18..24657e875 100644 --- a/src/iperf_locale.c +++ b/src/iperf_locale.c @@ -202,6 +202,9 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n" " --dscp N or --dscp val set the IP dscp value, either 0-63 or symbolic.\n" " Numeric values can be specified in decimal,\n" " octal and hex (see --tos above).\n" +#if defined(HAVE_SO_PRIORITY) + " --sock-prio N set the socket priority (only supported on Linux)\n" +#endif /* HAVE_SO_PRIORITY */ #if defined(HAVE_FLOWLABEL) " -L, --flowlabel N set the IPv6 flow label (only supported on Linux)\n" #endif /* HAVE_FLOWLABEL */