X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=rdma%2Futils.c;h=4d3de4fadba2e5deb5a496988bae6486a5f0ef0d;hb=7ded3c97b9836f2ae0bf21fc79d8559357acb190;hp=7b2001e2091d3b5eb5c075b978d927ada4c7a6b3;hpb=6a21ca8a4aa44fa1826b3878f42e6b0a1b90bae0;p=mirror_iproute2.git diff --git a/rdma/utils.c b/rdma/utils.c index 7b2001e2..4d3de4fa 100644 --- a/rdma/utils.c +++ b/rdma/utils.c @@ -1,17 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB /* * utils.c RDMA tool - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * * Authors: Leon Romanovsky */ #include "rdma.h" +#include +#include -static int rd_argc(struct rd *rd) +int rd_argc(struct rd *rd) { return rd->argc; } @@ -23,7 +20,7 @@ char *rd_argv(struct rd *rd) return *rd->argv; } -static int strcmpx(const char *str1, const char *str2) +int strcmpx(const char *str1, const char *str2) { if (strlen(str1) > strlen(str2)) return -1; @@ -50,13 +47,46 @@ bool rd_no_arg(struct rd *rd) return rd_argc(rd) == 0; } -uint32_t get_port_from_argv(struct rd *rd) +/* + * Possible input:output + * dev/port | first port | is_dump_all + * mlx5_1 | 0 | true + * mlx5_1/ | 0 | true + * mlx5_1/0 | 0 | false + * mlx5_1/1 | 1 | false + * mlx5_1/- | 0 | false + * + * In strict port mode, a non-0 port must be provided + */ +static int get_port_from_argv(struct rd *rd, uint32_t *port, + bool *is_dump_all, bool strict_port) { char *slash; + *port = 0; + *is_dump_all = strict_port ? false : true; + slash = strchr(rd_argv(rd), '/'); /* if no port found, return 0 */ - return slash ? atoi(slash + 1) : 0; + if (slash++) { + if (*slash == '-') { + if (strict_port) + return -EINVAL; + *is_dump_all = false; + return 0; + } + + if (isdigit(*slash)) { + *is_dump_all = false; + *port = atoi(slash); + } + if (!*port && strlen(slash)) + return -EINVAL; + } + if (strict_port && (*port == 0)) + return -EINVAL; + + return 0; } static struct dev_map *dev_map_alloc(const char *dev_name) @@ -67,6 +97,10 @@ static struct dev_map *dev_map_alloc(const char *dev_name) if (!dev_map) return NULL; dev_map->dev_name = strdup(dev_name); + if (!dev_map->dev_name) { + free(dev_map); + return NULL; + } return dev_map; } @@ -83,6 +117,272 @@ static void dev_map_cleanup(struct rd *rd) } } +static int add_filter(struct rd *rd, char *key, char *value, + const struct filters valid_filters[]) +{ + char cset[] = "1234567890,-"; + struct filter_entry *fe; + bool key_found = false; + int idx = 0; + char *endp; + int ret; + + fe = calloc(1, sizeof(*fe)); + if (!fe) + return -ENOMEM; + + while (idx < MAX_NUMBER_OF_FILTERS && valid_filters[idx].name) { + if (!strcmpx(key, valid_filters[idx].name)) { + key_found = true; + break; + } + idx++; + } + if (!key_found) { + pr_err("Unsupported filter option: %s\n", key); + ret = -EINVAL; + goto err; + } + + /* + * Check the filter validity, not optimal, but works + * + * Actually, there are three types of filters + * numeric - for example PID or QPN + * string - for example states + * link - user requested to filter on specific link + * e.g. mlx5_1/1, mlx5_1/-, mlx5_1 ... + */ + if (valid_filters[idx].is_number && + strspn(value, cset) != strlen(value)) { + pr_err("%s filter accepts \"%s\" characters only\n", key, cset); + ret = -EINVAL; + goto err; + } + + fe->key = strdup(key); + fe->value = strdup(value); + if (!fe->key || !fe->value) { + ret = -ENOMEM; + goto err_alloc; + } + + errno = 0; + strtol(fe->value, &endp, 10); + if (valid_filters[idx].is_doit && !errno && *endp == '\0') + fe->is_doit = true; + + for (idx = 0; idx < strlen(fe->value); idx++) + fe->value[idx] = tolower(fe->value[idx]); + + list_add_tail(&fe->list, &rd->filter_list); + return 0; + +err_alloc: + free(fe->value); + free(fe->key); +err: + free(fe); + return ret; +} + +bool rd_doit_index(struct rd *rd, uint32_t *idx) +{ + struct filter_entry *fe; + + list_for_each_entry(fe, &rd->filter_list, list) { + if (fe->is_doit) { + *idx = atoi(fe->value); + return true; + } + } + + return false; +} + +int rd_build_filter(struct rd *rd, const struct filters valid_filters[]) +{ + int ret = 0; + int idx = 0; + + if (!valid_filters || !rd_argc(rd)) + goto out; + + if (rd_argc(rd) == 1) { + pr_err("No filter data was supplied to filter option %s\n", rd_argv(rd)); + ret = -EINVAL; + goto out; + } + + if (rd_argc(rd) % 2) { + pr_err("There is filter option without data\n"); + ret = -EINVAL; + goto out; + } + + while (idx != rd_argc(rd)) { + /* + * We can do micro-optimization and skip "dev" + * and "link" filters, but it is not worth of it. + */ + ret = add_filter(rd, *(rd->argv + idx), + *(rd->argv + idx + 1), valid_filters); + if (ret) + goto out; + idx += 2; + } + +out: + return ret; +} + +static bool rd_check_is_key_exist(struct rd *rd, const char *key) +{ + struct filter_entry *fe; + + list_for_each_entry(fe, &rd->filter_list, list) { + if (!strcmpx(fe->key, key)) + return true; + } + + return false; +} + +/* + * Check if string entry is filtered: + * * key doesn't exist -> user didn't request -> not filtered + */ +static bool rd_check_is_string_filtered(struct rd *rd, const char *key, + const char *val) +{ + bool key_is_filtered = false; + struct filter_entry *fe; + char *p = NULL; + char *str; + + list_for_each_entry(fe, &rd->filter_list, list) { + if (!strcmpx(fe->key, key)) { + /* We found the key */ + p = strdup(fe->value); + key_is_filtered = true; + if (!p) { + /* + * Something extremely wrong if we fail + * to allocate small amount of bytes. + */ + pr_err("Found key, but failed to allocate memory to store value\n"); + return key_is_filtered; + } + + /* + * Need to check if value in range + * It can come in the following formats + * and their permutations: + * str + * str1,str2 + */ + str = strtok(p, ","); + while (str) { + if (strlen(str) == strlen(val) && + !strcasecmp(str, val)) { + key_is_filtered = false; + goto out; + } + str = strtok(NULL, ","); + } + goto out; + } + } + +out: + free(p); + return key_is_filtered; +} + +/* + * Check if key is filtered: + * key doesn't exist -> user didn't request -> not filtered + */ +static bool rd_check_is_filtered(struct rd *rd, const char *key, uint32_t val) +{ + bool key_is_filtered = false; + struct filter_entry *fe; + + list_for_each_entry(fe, &rd->filter_list, list) { + uint32_t left_val = 0, fe_value = 0; + bool range_check = false; + char *p = fe->value; + + if (!strcmpx(fe->key, key)) { + /* We found the key */ + key_is_filtered = true; + /* + * Need to check if value in range + * It can come in the following formats + * (and their permutations): + * numb + * numb1,numb2 + * ,numb1,numb2 + * numb1-numb2 + * numb1,numb2-numb3,numb4-numb5 + */ + while (*p) { + if (isdigit(*p)) { + fe_value = strtol(p, &p, 10); + if (fe_value == val || + (range_check && left_val < val && + val < fe_value)) { + key_is_filtered = false; + goto out; + } + range_check = false; + } else { + if (*p == '-') { + left_val = fe_value; + range_check = true; + } + p++; + } + } + goto out; + } + } + +out: + return key_is_filtered; +} + +bool rd_is_filtered_attr(struct rd *rd, const char *key, uint32_t val, + struct nlattr *attr) +{ + if (!attr) + return rd_check_is_key_exist(rd, key); + + return rd_check_is_filtered(rd, key, val); +} + +bool rd_is_string_filtered_attr(struct rd *rd, const char *key, const char *val, + struct nlattr *attr) +{ + if (!attr) + rd_check_is_key_exist(rd, key); + + return rd_check_is_string_filtered(rd, key, val); +} + +static void filters_cleanup(struct rd *rd) +{ + struct filter_entry *fe, *tmp; + + list_for_each_entry_safe(fe, tmp, + &rd->filter_list, list) { + list_del(&fe->list); + free(fe->key); + free(fe->value); + free(fe); + } +} + static const enum mnl_attr_data_type nldev_policy[RDMA_NLDEV_ATTR_MAX] = { [RDMA_NLDEV_ATTR_DEV_INDEX] = MNL_TYPE_U32, [RDMA_NLDEV_ATTR_DEV_NAME] = MNL_TYPE_NUL_STRING, @@ -97,11 +397,64 @@ static const enum mnl_attr_data_type nldev_policy[RDMA_NLDEV_ATTR_MAX] = { [RDMA_NLDEV_ATTR_PORT_STATE] = MNL_TYPE_U8, [RDMA_NLDEV_ATTR_PORT_PHYS_STATE] = MNL_TYPE_U8, [RDMA_NLDEV_ATTR_DEV_NODE_TYPE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_SUMMARY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_NAME] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_RES_SUMMARY_ENTRY_CURR] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_RES_QP] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_QP_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_LQPN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_RQPN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_RQ_PSN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_SQ_PSN] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_PATH_MIG_STATE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_TYPE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_STATE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_PID] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_KERN_NAME] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_RES_CM_ID] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_CM_ID_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_PS] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_SRC_ADDR] = MNL_TYPE_UNSPEC, + [RDMA_NLDEV_ATTR_RES_DST_ADDR] = MNL_TYPE_UNSPEC, + [RDMA_NLDEV_ATTR_RES_CQ] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_CQ_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_CQE] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_USECNT] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_RES_POLL_CTX] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_MR] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_MR_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_RES_RKEY] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_LKEY] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_RES_IOVA] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_RES_MRLEN] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_NDEV_INDEX] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_NDEV_NAME] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_DRIVER] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_DRIVER_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_DRIVER_STRING] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_DRIVER_PRINT_TYPE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_DRIVER_S32] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_DRIVER_U32] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_DRIVER_S64] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_DRIVER_U64] = MNL_TYPE_U64, + [RDMA_NLDEV_SYS_ATTR_NETNS_MODE] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_STAT_COUNTER] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_STAT_COUNTER_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_STAT_COUNTER_ID] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_STAT_HWCOUNTERS] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY] = MNL_TYPE_NESTED, + [RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_NAME] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_STAT_HWCOUNTER_ENTRY_VALUE] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_STAT_MODE] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_STAT_RES] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_STAT_AUTO_MODE_MASK] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_DEV_DIM] = MNL_TYPE_U8, + [RDMA_NLDEV_ATTR_RES_RAW] = MNL_TYPE_BINARY, }; -int rd_attr_cb(const struct nlattr *attr, void *data) +int rd_attr_check(const struct nlattr *attr, int *typep) { - const struct nlattr **tb = data; int type; if (mnl_attr_type_valid(attr, RDMA_NLDEV_ATTR_MAX) < 0) @@ -109,6 +462,24 @@ int rd_attr_cb(const struct nlattr *attr, void *data) type = mnl_attr_get_type(attr); + if (mnl_attr_validate(attr, nldev_policy[type]) < 0) + return MNL_CB_ERROR; + + *typep = nldev_policy[type]; + return MNL_CB_OK; +} + +int rd_attr_cb(const struct nlattr *attr, void *data) +{ + const struct nlattr **tb = data; + int type; + + if (mnl_attr_type_valid(attr, RDMA_NLDEV_ATTR_MAX - 1) < 0) + /* We received unknown attribute */ + return MNL_CB_OK; + + type = mnl_attr_get_type(attr); + if (mnl_attr_validate(attr, nldev_policy[type]) < 0) return MNL_CB_ERROR; @@ -150,20 +521,40 @@ void rd_free(struct rd *rd) return; free(rd->buff); dev_map_cleanup(rd); + filters_cleanup(rd); } -int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd)) +int rd_set_arg_to_devname(struct rd *rd) +{ + int ret = 0; + + while (!rd_no_arg(rd)) { + if (rd_argv_match(rd, "dev") || rd_argv_match(rd, "link")) { + rd_arg_inc(rd); + if (rd_no_arg(rd)) { + pr_err("No device name was supplied\n"); + ret = -EINVAL; + } + goto out; + } + rd_arg_inc(rd); + } +out: + return ret; +} + +int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd), bool strict_port) { struct dev_map *dev_map; uint32_t port; int ret = 0; - if (rd->json_output) - jsonw_start_array(rd->jw); + new_json_obj(rd->json_output); if (rd_no_arg(rd)) { list_for_each_entry(dev_map, &rd->dev_map_list, list) { rd->dev_idx = dev_map->idx; - for (port = 1; port < dev_map->num_ports + 1; port++) { + port = (strict_port) ? 1 : 0; + for (; port < dev_map->num_ports + 1; port++) { rd->port_idx = port; ret = cb(rd); if (ret) @@ -172,21 +563,23 @@ int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd)) } } else { + bool is_dump_all; + dev_map = dev_map_lookup(rd, true); - port = get_port_from_argv(rd); - if (!dev_map || port > dev_map->num_ports) { + ret = get_port_from_argv(rd, &port, &is_dump_all, strict_port); + if (!dev_map || port > dev_map->num_ports || (!port && ret)) { pr_err("Wrong device name\n"); ret = -ENOENT; goto out; } rd_arg_inc(rd); rd->dev_idx = dev_map->idx; - rd->port_idx = port ? : 1; + rd->port_idx = port; for (; rd->port_idx < dev_map->num_ports + 1; rd->port_idx++) { ret = cb(rd); if (ret) goto out; - if (port) + if (!is_dump_all) /* * We got request to show link for devname * with port index. @@ -196,8 +589,7 @@ int rd_exec_link(struct rd *rd, int (*cb)(struct rd *rd)) } out: - if (rd->json_output) - jsonw_end_array(rd->jw); + delete_json_obj(); return ret; } @@ -206,8 +598,7 @@ int rd_exec_dev(struct rd *rd, int (*cb)(struct rd *rd)) struct dev_map *dev_map; int ret = 0; - if (rd->json_output) - jsonw_start_array(rd->jw); + new_json_obj(rd->json_output); if (rd_no_arg(rd)) { list_for_each_entry(dev_map, &rd->dev_map_list, list) { rd->dev_idx = dev_map->idx; @@ -227,11 +618,20 @@ int rd_exec_dev(struct rd *rd, int (*cb)(struct rd *rd)) ret = cb(rd); } out: - if (rd->json_output) - jsonw_end_array(rd->jw); + delete_json_obj(); return ret; } +int rd_exec_require_dev(struct rd *rd, int (*cb)(struct rd *rd)) +{ + if (rd_no_arg(rd)) { + pr_err("Please provide device name.\n"); + return -EINVAL; + } + + return rd_exec_dev(rd, cb); +} + int rd_exec_cmd(struct rd *rd, const struct rd_cmd *cmds, const char *str) { const struct rd_cmd *c; @@ -305,10 +705,28 @@ int rd_recv_msg(struct rd *rd, mnl_cb_t callback, void *data, unsigned int seq) ret = mnl_cb_run(buf, ret, seq, portid, callback, data); } while (ret > 0); + if (ret < 0 && !rd->suppress_errors) + perror("error"); + mnl_socket_close(rd->nl); return ret; } +static int null_cb(const struct nlmsghdr *nlh, void *data) +{ + return MNL_CB_OK; +} + +int rd_sendrecv_msg(struct rd *rd, unsigned int seq) +{ + int ret; + + ret = rd_send_msg(rd); + if (!ret) + ret = rd_recv_msg(rd, null_cb, rd, seq); + return ret; +} + static struct dev_map *_dev_map_lookup(struct rd *rd, const char *dev_name) { struct dev_map *dev_map; @@ -340,3 +758,202 @@ struct dev_map *dev_map_lookup(struct rd *rd, bool allow_port_index) free(dev_name); return dev_map; } + +#define nla_type(attr) ((attr)->nla_type & NLA_TYPE_MASK) + +void newline(struct rd *rd) +{ + close_json_object(); + print_color_string(PRINT_FP, COLOR_NONE, NULL, "\n", NULL); +} + +void newline_indent(struct rd *rd) +{ + newline(rd); + print_color_string(PRINT_FP, COLOR_NONE, NULL, " ", NULL); +} + +static int print_driver_string(struct rd *rd, const char *key_str, + const char *val_str) +{ + print_color_string(PRINT_ANY, COLOR_NONE, key_str, key_str, val_str); + print_color_string(PRINT_FP, COLOR_NONE, NULL, " %s ", val_str); + return 0; +} + +void print_on_off(struct rd *rd, const char *key_str, bool on) +{ + print_driver_string(rd, key_str, (on) ? "on":"off"); +} + +static int print_driver_s32(struct rd *rd, const char *key_str, int32_t val, + enum rdma_nldev_print_type print_type) +{ + if (!rd->json_output) { + switch (print_type) { + case RDMA_NLDEV_PRINT_TYPE_UNSPEC: + return pr_out("%s %d ", key_str, val); + case RDMA_NLDEV_PRINT_TYPE_HEX: + return pr_out("%s 0x%x ", key_str, val); + default: + return -EINVAL; + } + } + print_color_int(PRINT_JSON, COLOR_NONE, key_str, NULL, val); + return 0; +} + +static int print_driver_u32(struct rd *rd, const char *key_str, uint32_t val, + enum rdma_nldev_print_type print_type) +{ + if (!rd->json_output) { + switch (print_type) { + case RDMA_NLDEV_PRINT_TYPE_UNSPEC: + return pr_out("%s %u ", key_str, val); + case RDMA_NLDEV_PRINT_TYPE_HEX: + return pr_out("%s 0x%x ", key_str, val); + default: + return -EINVAL; + } + } + print_color_int(PRINT_JSON, COLOR_NONE, key_str, NULL, val); + return 0; +} + +static int print_driver_s64(struct rd *rd, const char *key_str, int64_t val, + enum rdma_nldev_print_type print_type) +{ + if (!rd->json_output) { + switch (print_type) { + case RDMA_NLDEV_PRINT_TYPE_UNSPEC: + return pr_out("%s %" PRId64 " ", key_str, val); + case RDMA_NLDEV_PRINT_TYPE_HEX: + return pr_out("%s 0x%" PRIx64 " ", key_str, val); + default: + return -EINVAL; + } + } + print_color_int(PRINT_JSON, COLOR_NONE, key_str, NULL, val); + return 0; +} + +static int print_driver_u64(struct rd *rd, const char *key_str, uint64_t val, + enum rdma_nldev_print_type print_type) +{ + if (!rd->json_output) { + switch (print_type) { + case RDMA_NLDEV_PRINT_TYPE_UNSPEC: + return pr_out("%s %" PRIu64 " ", key_str, val); + case RDMA_NLDEV_PRINT_TYPE_HEX: + return pr_out("%s 0x%" PRIx64 " ", key_str, val); + default: + return -EINVAL; + } + } + print_color_int(PRINT_JSON, COLOR_NONE, key_str, NULL, val); + return 0; +} + +static int print_driver_entry(struct rd *rd, struct nlattr *key_attr, + struct nlattr *val_attr, + enum rdma_nldev_print_type print_type) +{ + int attr_type = nla_type(val_attr); + int ret = -EINVAL; + char *key_str; + + if (asprintf(&key_str, "drv_%s", mnl_attr_get_str(key_attr)) == -1) + return -ENOMEM; + + switch (attr_type) { + case RDMA_NLDEV_ATTR_DRIVER_STRING: + ret = print_driver_string(rd, key_str, + mnl_attr_get_str(val_attr)); + break; + case RDMA_NLDEV_ATTR_DRIVER_S32: + ret = print_driver_s32(rd, key_str, mnl_attr_get_u32(val_attr), + print_type); + break; + case RDMA_NLDEV_ATTR_DRIVER_U32: + ret = print_driver_u32(rd, key_str, mnl_attr_get_u32(val_attr), + print_type); + break; + case RDMA_NLDEV_ATTR_DRIVER_S64: + ret = print_driver_s64(rd, key_str, mnl_attr_get_u64(val_attr), + print_type); + break; + case RDMA_NLDEV_ATTR_DRIVER_U64: + ret = print_driver_u64(rd, key_str, mnl_attr_get_u64(val_attr), + print_type); + break; + } + free(key_str); + return ret; +} + +void print_raw_data(struct rd *rd, struct nlattr **nla_line) +{ + uint8_t *data; + uint32_t len; + int i = 0; + + if (!rd->show_raw) + return; + + len = mnl_attr_get_payload_len(nla_line[RDMA_NLDEV_ATTR_RES_RAW]); + data = mnl_attr_get_payload(nla_line[RDMA_NLDEV_ATTR_RES_RAW]); + open_json_array(PRINT_JSON, "data"); + while (i < len) { + print_color_uint(PRINT_ANY, COLOR_NONE, NULL, "%d", data[i]); + i++; + } + close_json_array(PRINT_ANY, ">"); +} + +void print_driver_table(struct rd *rd, struct nlattr *tb) +{ + int print_type = RDMA_NLDEV_PRINT_TYPE_UNSPEC; + struct nlattr *tb_entry, *key = NULL, *val; + int type, cc = 0; + int ret; + + if (!rd->show_driver_details || !tb) + return; + + if (rd->pretty_output) + newline_indent(rd); + + /* + * Driver attrs are tuples of {key, [print-type], value}. + * The key must be a string. If print-type is present, it + * defines an alternate printf format type vs the native format + * for the attribute. And the value can be any available + * driver type. + */ + mnl_attr_for_each_nested(tb_entry, tb) { + + if (cc > MAX_LINE_LENGTH) { + if (rd->pretty_output) + newline_indent(rd); + cc = 0; + } + if (rd_attr_check(tb_entry, &type) != MNL_CB_OK) + return; + if (!key) { + if (type != MNL_TYPE_NUL_STRING) + return; + key = tb_entry; + } else if (type == MNL_TYPE_U8) { + print_type = mnl_attr_get_u8(tb_entry); + } else { + val = tb_entry; + ret = print_driver_entry(rd, key, val, print_type); + if (ret < 0) + return; + cc += ret; + print_type = RDMA_NLDEV_PRINT_TYPE_UNSPEC; + key = NULL; + } + } + return; +}