/* * dev.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" static int dev_help(struct rd *rd) { pr_out("Usage: %s dev show [DEV]\n", rd->filename); return 0; } static const char *dev_caps_to_str(uint32_t idx) { #define RDMA_DEV_FLAGS(x) \ x(RESIZE_MAX_WR, 0) \ x(BAD_PKEY_CNTR, 1) \ x(BAD_QKEY_CNTR, 2) \ x(RAW_MULTI, 3) \ x(AUTO_PATH_MIG, 4) \ x(CHANGE_PHY_PORT, 5) \ x(UD_AV_PORT_ENFORCE_PORT_ENFORCE, 6) \ x(CURR_QP_STATE_MOD, 7) \ x(SHUTDOWN_PORT, 8) \ x(INIT_TYPE, 9) \ x(PORT_ACTIVE_EVENT, 10) \ x(SYS_IMAGE_GUID, 11) \ x(RC_RNR_NAK_GEN, 12) \ x(SRQ_RESIZE, 13) \ x(N_NOTIFY_CQ, 14) \ x(LOCAL_DMA_LKEY, 15) \ x(MEM_WINDOW, 17) \ x(UD_IP_CSUM, 18) \ x(UD_TSO, 19) \ x(XRC, 20) \ x(MEM_MGT_EXTENSIONS, 21) \ x(BLOCK_MULTICAST_LOOPBACK, 22) \ x(MEM_WINDOW_TYPE_2A, 23) \ x(MEM_WINDOW_TYPE_2B, 24) \ x(RC_IP_CSUM, 25) \ x(RAW_IP_CSUM, 26) \ x(CROSS_CHANNEL, 27) \ x(MANAGED_FLOW_STEERING, 29) \ x(SIGNATURE_HANDOVER, 30) \ x(ON_DEMAND_PAGING, 31) \ x(SG_GAPS_REG, 32) \ x(VIRTUAL_FUNCTION, 33) \ x(RAW_SCATTER_FCS, 34) \ x(RDMA_NETDEV_OPA_VNIC, 35) enum { RDMA_DEV_FLAGS(RDMA_BITMAP_ENUM) }; static const char * const rdma_dev_names[] = { RDMA_DEV_FLAGS(RDMA_BITMAP_NAMES) }; #undef RDMA_DEV_FLAGS if (idx < ARRAY_SIZE(rdma_dev_names) && rdma_dev_names[idx]) return rdma_dev_names[idx]; return "UNKNOWN"; } static void dev_print_caps(struct rd *rd, struct nlattr **tb) { uint64_t caps; uint32_t idx; if (!tb[RDMA_NLDEV_ATTR_CAP_FLAGS]) return; caps = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_CAP_FLAGS]); if (rd->json_output) { jsonw_name(rd->jw, "caps"); jsonw_start_array(rd->jw); } else { pr_out("\n caps: <"); } for (idx = 0; caps; idx++) { if (caps & 0x1) { if (rd->json_output) { jsonw_string(rd->jw, dev_caps_to_str(idx)); } else { pr_out("%s", dev_caps_to_str(idx)); if (caps >> 0x1) pr_out(", "); } } caps >>= 0x1; } if (rd->json_output) jsonw_end_array(rd->jw); else pr_out(">"); } static void dev_print_fw(struct rd *rd, struct nlattr **tb) { const char *str; if (!tb[RDMA_NLDEV_ATTR_FW_VERSION]) return; str = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_FW_VERSION]); if (rd->json_output) jsonw_string_field(rd->jw, "fw", str); else pr_out("fw %s ", str); } static void dev_print_node_guid(struct rd *rd, struct nlattr **tb) { uint64_t node_guid; uint16_t vp[4]; char str[32]; if (!tb[RDMA_NLDEV_ATTR_NODE_GUID]) return; node_guid = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_NODE_GUID]); memcpy(vp, &node_guid, sizeof(uint64_t)); snprintf(str, 32, "%04x:%04x:%04x:%04x", vp[3], vp[2], vp[1], vp[0]); if (rd->json_output) jsonw_string_field(rd->jw, "node_guid", str); else pr_out("node_guid %s ", str); } static void dev_print_sys_image_guid(struct rd *rd, struct nlattr **tb) { uint64_t sys_image_guid; uint16_t vp[4]; char str[32]; if (!tb[RDMA_NLDEV_ATTR_SYS_IMAGE_GUID]) return; sys_image_guid = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_SYS_IMAGE_GUID]); memcpy(vp, &sys_image_guid, sizeof(uint64_t)); snprintf(str, 32, "%04x:%04x:%04x:%04x", vp[3], vp[2], vp[1], vp[0]); if (rd->json_output) jsonw_string_field(rd->jw, "sys_image_guid", str); else pr_out("sys_image_guid %s ", str); } static const char *node_type_to_str(uint8_t node_type) { static const char * const node_type_str[] = { "unknown", "ca", "switch", "router", "rnic", "usnic", "usnic_dp" }; if (node_type < ARRAY_SIZE(node_type_str)) return node_type_str[node_type]; return "unknown"; } static void dev_print_node_type(struct rd *rd, struct nlattr **tb) { const char *node_str; uint8_t node_type; if (!tb[RDMA_NLDEV_ATTR_DEV_NODE_TYPE]) return; node_type = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_DEV_NODE_TYPE]); node_str = node_type_to_str(node_type); if (rd->json_output) jsonw_string_field(rd->jw, "node_type", node_str); else pr_out("node_type %s ", node_str); } static int dev_parse_cb(const struct nlmsghdr *nlh, void *data) { struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; struct rd *rd = data; const char *name; uint32_t idx; mnl_attr_parse(nlh, 0, rd_attr_cb, tb); if (!tb[RDMA_NLDEV_ATTR_DEV_INDEX] || !tb[RDMA_NLDEV_ATTR_DEV_NAME]) return MNL_CB_ERROR; idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); if (rd->json_output) { jsonw_uint_field(rd->jw, "ifindex", idx); jsonw_string_field(rd->jw, "ifname", name); } else { pr_out("%u: %s: ", idx, name); } dev_print_node_type(rd, tb); dev_print_fw(rd, tb); dev_print_node_guid(rd, tb); dev_print_sys_image_guid(rd, tb); if (rd->show_details) dev_print_caps(rd, tb); if (!rd->json_output) pr_out("\n"); return MNL_CB_OK; } static int dev_no_args(struct rd *rd) { uint32_t seq; int ret; rd_prepare_msg(rd, RDMA_NLDEV_CMD_GET, &seq, (NLM_F_REQUEST | NLM_F_ACK)); mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx); ret = rd_send_msg(rd); if (ret) return ret; if (rd->json_output) jsonw_start_object(rd->jw); ret = rd_recv_msg(rd, dev_parse_cb, rd, seq); if (rd->json_output) jsonw_end_object(rd->jw); return ret; } static int dev_one_show(struct rd *rd) { const struct rd_cmd cmds[] = { { NULL, dev_no_args}, { 0 } }; return rd_exec_cmd(rd, cmds, "parameter"); } static int dev_show(struct rd *rd) { struct dev_map *dev_map; int ret = 0; if (rd->json_output) jsonw_start_array(rd->jw); if (rd_no_arg(rd)) { list_for_each_entry(dev_map, &rd->dev_map_list, list) { rd->dev_idx = dev_map->idx; ret = dev_one_show(rd); if (ret) goto out; } } else { dev_map = dev_map_lookup(rd, false); if (!dev_map) { pr_err("Wrong device name\n"); ret = -ENOENT; goto out; } rd_arg_inc(rd); rd->dev_idx = dev_map->idx; ret = dev_one_show(rd); } out: if (rd->json_output) jsonw_end_array(rd->jw); return ret; } int cmd_dev(struct rd *rd) { const struct rd_cmd cmds[] = { { NULL, dev_show }, { "show", dev_show }, { "list", dev_show }, { "help", dev_help }, { 0 } }; return rd_exec_cmd(rd, cmds, "dev command"); }