diff options
author | Petr Machata <me@pmachata.org> | 2020-12-11 00:02:22 +0100 |
---|---|---|
committer | David Ahern <dsahern@gmail.com> | 2020-12-14 16:41:58 +0000 |
commit | 6567cb588b15883d7b0bdc9fcc9d2a7a8f37fa3e (patch) | |
tree | 5673e5facd7c6763470fdbf1c034b187d71df5fa /dcb | |
parent | 808dd741fce1158e824ae5bdf367947ae764be19 (diff) | |
download | iproute2-6567cb588b15883d7b0bdc9fcc9d2a7a8f37fa3e.tar.gz |
dcb: Add a subtool for the DCB PFC object
PFC, for "Priority-based Flow Control", allows configuration of priority
lossiness, and related toggles.
Add a dcb subtool to allow showing and tweaking of individual PFC
configuration options, and querying statistics. For example:
# dcb pfc show dev eni1np1
pfc-cap 8 macsec-bypass on delay 0
pg-pfc 0:off 1:on 2:off 3:off 4:off 5:off 6:off 7:on
requests 0:0 1:217 2:0 3:0 4:0 5:0 6:0 7:28
indications 0:0 1:179 2:0 3:0 4:0 5:0 6:0 7:18
Signed-off-by: Petr Machata <me@pmachata.org>
Signed-off-by: David Ahern <dsahern@gmail.com>
Diffstat (limited to 'dcb')
-rw-r--r-- | dcb/Makefile | 2 | ||||
-rw-r--r-- | dcb/dcb.c | 27 | ||||
-rw-r--r-- | dcb/dcb.h | 6 | ||||
-rw-r--r-- | dcb/dcb_pfc.c | 286 |
4 files changed, 319 insertions, 2 deletions
diff --git a/dcb/Makefile b/dcb/Makefile index 89581716..ea557a30 100644 --- a/dcb/Makefile +++ b/dcb/Makefile @@ -5,7 +5,7 @@ TARGETS := ifeq ($(HAVE_MNL),y) -DCBOBJ = dcb.o dcb_ets.o +DCBOBJ = dcb.o dcb_ets.o dcb_pfc.o TARGETS += dcb endif @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ +#include <inttypes.h> #include <stdio.h> #include <linux/dcbnl.h> #include <libmnl/libmnl.h> @@ -201,6 +202,28 @@ void dcb_print_array_u8(const __u8 *array, size_t size) } } +void dcb_print_array_u64(const __u64 *array, size_t size) +{ + SPRINT_BUF(b); + size_t i; + + for (i = 0; i < size; i++) { + snprintf(b, sizeof(b), "%zd:%%" PRIu64 " ", i); + print_u64(PRINT_ANY, NULL, b, array[i]); + } +} + +void dcb_print_array_on_off(const __u8 *array, size_t size) +{ + SPRINT_BUF(b); + size_t i; + + for (i = 0; i < size; i++) { + snprintf(b, sizeof(b), "%zd:%%s ", i); + print_on_off(PRINT_ANY, NULL, b, array[i]); + } +} + void dcb_print_array_kw(const __u8 *array, size_t array_size, const char *const kw[], size_t kw_size) { @@ -309,7 +332,7 @@ static void dcb_help(void) fprintf(stderr, "Usage: dcb [ OPTIONS ] OBJECT { COMMAND | help }\n" " dcb [ -f | --force ] { -b | --batch } filename [ -N | --Netns ] netnsname\n" - "where OBJECT := ets\n" + "where OBJECT := { ets | pfc }\n" " OPTIONS := [ -V | --Version | -i | --iec | -j | --json\n" " | -p | --pretty | -s | --statistics | -v | --verbose]\n"); } @@ -321,6 +344,8 @@ static int dcb_cmd(struct dcb *dcb, int argc, char **argv) return 0; } else if (matches(*argv, "ets") == 0) { return dcb_cmd_ets(dcb, argc - 1, argv + 1); + } else if (matches(*argv, "pfc") == 0) { + return dcb_cmd_pfc(dcb, argc - 1, argv + 1); } fprintf(stderr, "Object \"%s\" is unknown\n", *argv); @@ -37,6 +37,8 @@ void dcb_print_named_array(const char *json_name, const char *fp_name, const __u8 *array, size_t size, void (*print_array)(const __u8 *, size_t)); void dcb_print_array_u8(const __u8 *array, size_t size); +void dcb_print_array_u64(const __u64 *array, size_t size); +void dcb_print_array_on_off(const __u8 *array, size_t size); void dcb_print_array_kw(const __u8 *array, size_t array_size, const char *const kw[], size_t kw_size); @@ -44,4 +46,8 @@ void dcb_print_array_kw(const __u8 *array, size_t array_size, int dcb_cmd_ets(struct dcb *dcb, int argc, char **argv); +/* dcb_pfc.c */ + +int dcb_cmd_pfc(struct dcb *dcb, int argc, char **argv); + #endif /* __DCB_H__ */ diff --git a/dcb/dcb_pfc.c b/dcb/dcb_pfc.c new file mode 100644 index 00000000..aaa09022 --- /dev/null +++ b/dcb/dcb_pfc.c @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <errno.h> +#include <stdio.h> +#include <linux/dcbnl.h> + +#include "dcb.h" +#include "utils.h" + +static void dcb_pfc_help_set(void) +{ + fprintf(stderr, + "Usage: dcb pfc set dev STRING\n" + " [ prio-pfc PFC-MAP ]\n" + " [ macsec-bypass { on | off } ]\n" + " [ delay INTEGER ]\n" + "\n" + " where PFC-MAP := [ PFC-MAP ] PFC-MAPPING\n" + " PFC-MAPPING := { all | TC }:PFC\n" + " TC := { 0 .. 7 }\n" + " PFC := { on | off }\n" + "\n" + ); +} + +static void dcb_pfc_help_show(void) +{ + fprintf(stderr, + "Usage: dcb [ -s ] pfc show dev STRING\n" + " [ pfc-cap ] [ prio-pfc ] [ macsec-bypass ]\n" + " [ delay ] [ requests ] [ indications ]\n" + "\n" + ); +} + +static void dcb_pfc_help(void) +{ + fprintf(stderr, + "Usage: dcb pfc help\n" + "\n" + ); + dcb_pfc_help_show(); + dcb_pfc_help_set(); +} + +static void dcb_pfc_to_array(__u8 array[IEEE_8021QAZ_MAX_TCS], __u8 pfc_en) +{ + int i; + + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) + array[i] = !!(pfc_en & (1 << i)); +} + +static void dcb_pfc_from_array(__u8 array[IEEE_8021QAZ_MAX_TCS], __u8 *pfc_en_p) +{ + __u8 pfc_en = 0; + int i; + + for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { + if (array[i]) + pfc_en |= 1 << i; + } + + *pfc_en_p = pfc_en; +} + +static int dcb_pfc_parse_mapping_prio_pfc(__u32 key, char *value, void *data) +{ + struct ieee_pfc *pfc = data; + __u8 pfc_en[IEEE_8021QAZ_MAX_TCS]; + bool enabled; + int ret; + + dcb_pfc_to_array(pfc_en, pfc->pfc_en); + + enabled = parse_on_off("PFC", value, &ret); + if (ret) + return ret; + + ret = dcb_parse_mapping("PRIO", key, IEEE_8021QAZ_MAX_TCS - 1, + "PFC", enabled, -1, + dcb_set_u8, pfc_en); + if (ret) + return ret; + + dcb_pfc_from_array(pfc_en, &pfc->pfc_en); + return 0; +} + +static void dcb_pfc_print_pfc_cap(const struct ieee_pfc *pfc) +{ + print_uint(PRINT_ANY, "pfc_cap", "pfc-cap %d ", pfc->pfc_cap); +} + +static void dcb_pfc_print_macsec_bypass(const struct ieee_pfc *pfc) +{ + print_on_off(PRINT_ANY, "macsec_bypass", "macsec-bypass %s ", pfc->mbc); +} + +static void dcb_pfc_print_delay(const struct ieee_pfc *pfc) +{ + print_uint(PRINT_ANY, "delay", "delay %d ", pfc->delay); +} + +static void dcb_pfc_print_prio_pfc(const struct ieee_pfc *pfc) +{ + __u8 pfc_en[IEEE_8021QAZ_MAX_TCS]; + + dcb_pfc_to_array(pfc_en, pfc->pfc_en); + dcb_print_named_array("prio_pfc", "prio-pfc", + pfc_en, ARRAY_SIZE(pfc_en), &dcb_print_array_on_off); +} + +static void dcb_pfc_print_requests(const struct ieee_pfc *pfc) +{ + open_json_array(PRINT_JSON, "requests"); + print_string(PRINT_FP, NULL, "requests ", NULL); + dcb_print_array_u64(pfc->requests, ARRAY_SIZE(pfc->requests)); + close_json_array(PRINT_JSON, "requests"); +} + +static void dcb_pfc_print_indications(const struct ieee_pfc *pfc) +{ + open_json_array(PRINT_JSON, "indications"); + print_string(PRINT_FP, NULL, "indications ", NULL); + dcb_print_array_u64(pfc->indications, ARRAY_SIZE(pfc->indications)); + close_json_array(PRINT_JSON, "indications"); +} + +static void dcb_pfc_print(const struct dcb *dcb, const struct ieee_pfc *pfc) +{ + dcb_pfc_print_pfc_cap(pfc); + dcb_pfc_print_macsec_bypass(pfc); + dcb_pfc_print_delay(pfc); + print_nl(); + + dcb_pfc_print_prio_pfc(pfc); + print_nl(); + + if (dcb->stats) { + dcb_pfc_print_requests(pfc); + print_nl(); + + dcb_pfc_print_indications(pfc); + print_nl(); + } +} + +static int dcb_pfc_get(struct dcb *dcb, const char *dev, struct ieee_pfc *pfc) +{ + return dcb_get_attribute(dcb, dev, DCB_ATTR_IEEE_PFC, pfc, sizeof(*pfc)); +} + +static int dcb_pfc_set(struct dcb *dcb, const char *dev, const struct ieee_pfc *pfc) +{ + return dcb_set_attribute(dcb, dev, DCB_ATTR_IEEE_PFC, pfc, sizeof(*pfc)); +} + +static int dcb_cmd_pfc_set(struct dcb *dcb, const char *dev, int argc, char **argv) +{ + struct ieee_pfc pfc; + int ret; + + if (!argc) { + dcb_pfc_help_set(); + return 0; + } + + ret = dcb_pfc_get(dcb, dev, &pfc); + if (ret) + return ret; + + do { + if (matches(*argv, "help") == 0) { + dcb_pfc_help_set(); + return 0; + } else if (matches(*argv, "prio-pfc") == 0) { + NEXT_ARG(); + ret = parse_mapping(&argc, &argv, true, + &dcb_pfc_parse_mapping_prio_pfc, &pfc); + if (ret) { + fprintf(stderr, "Invalid pfc mapping %s\n", *argv); + return ret; + } + continue; + } else if (matches(*argv, "macsec-bypass") == 0) { + NEXT_ARG(); + pfc.mbc = parse_on_off("macsec-bypass", *argv, &ret); + if (ret) + return ret; + } else if (matches(*argv, "delay") == 0) { + NEXT_ARG(); + /* Do not support the size notations for delay. + * Delay is specified in "bit times", not bits, so + * it is not applicable. At the same time it would + * be confusing that 10Kbit does not mean 10240, + * but 1280. + */ + if (get_u16(&pfc.delay, *argv, 0)) { + fprintf(stderr, "Invalid delay `%s', expected an integer 0..65535\n", + *argv); + return -EINVAL; + } + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + dcb_pfc_help_set(); + return -EINVAL; + } + + NEXT_ARG_FWD(); + } while (argc > 0); + + return dcb_pfc_set(dcb, dev, &pfc); +} + +static int dcb_cmd_pfc_show(struct dcb *dcb, const char *dev, int argc, char **argv) +{ + struct ieee_pfc pfc; + int ret; + + ret = dcb_pfc_get(dcb, dev, &pfc); + if (ret) + return ret; + + open_json_object(NULL); + + if (!argc) { + dcb_pfc_print(dcb, &pfc); + goto out; + } + + do { + if (matches(*argv, "help") == 0) { + dcb_pfc_help_show(); + return 0; + } else if (matches(*argv, "prio-pfc") == 0) { + dcb_pfc_print_prio_pfc(&pfc); + print_nl(); + } else if (matches(*argv, "pfc-cap") == 0) { + dcb_pfc_print_pfc_cap(&pfc); + print_nl(); + } else if (matches(*argv, "macsec-bypass") == 0) { + dcb_pfc_print_macsec_bypass(&pfc); + print_nl(); + } else if (matches(*argv, "delay") == 0) { + dcb_pfc_print_delay(&pfc); + print_nl(); + } else if (matches(*argv, "requests") == 0) { + dcb_pfc_print_requests(&pfc); + print_nl(); + } else if (matches(*argv, "indications") == 0) { + dcb_pfc_print_indications(&pfc); + print_nl(); + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + dcb_pfc_help_show(); + return -EINVAL; + } + + NEXT_ARG_FWD(); + } while (argc > 0); + +out: + close_json_object(); + return 0; +} + +int dcb_cmd_pfc(struct dcb *dcb, int argc, char **argv) +{ + if (!argc || matches(*argv, "help") == 0) { + dcb_pfc_help(); + return 0; + } else if (matches(*argv, "show") == 0) { + NEXT_ARG_FWD(); + return dcb_cmd_parse_dev(dcb, argc, argv, + dcb_cmd_pfc_show, dcb_pfc_help_show); + } else if (matches(*argv, "set") == 0) { + NEXT_ARG_FWD(); + return dcb_cmd_parse_dev(dcb, argc, argv, + dcb_cmd_pfc_set, dcb_pfc_help_set); + } else { + fprintf(stderr, "What is \"%s\"?\n", *argv); + dcb_pfc_help(); + return -EINVAL; + } +} |