blob: c597cba53e02f9e572013d61fe37c759b083af3d [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
/*
* dev.c RDMA tool
* Authors: Leon Romanovsky <leonro@mellanox.com>
*/
#include <fcntl.h>
#include "rdma.h"
static int dev_help(struct rd *rd)
{
pr_out("Usage: %s dev show [DEV]\n", rd->filename);
pr_out(" %s dev set [DEV] name DEVNAME\n", rd->filename);
pr_out(" %s dev set [DEV] netns NSNAME\n", rd->filename);
pr_out(" %s dev set [DEV] adaptive-moderation [on|off]\n", rd->filename);
return 0;
}
static const char *dev_caps_to_str(uint32_t idx)
{
#define RDMA_DEV_FLAGS_LOW(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)
#define RDMA_DEV_FLAGS_HIGH(x) \
x(SG_GAPS_REG, 0) \
x(VIRTUAL_FUNCTION, 1) \
x(RAW_SCATTER_FCS, 2) \
x(RDMA_NETDEV_OPA_VNIC, 3) \
x(PCI_WRITE_END_PADDING, 4)
/*
* Separation below is needed to allow compilation of rdmatool
* on 32bits systems. On such systems, C-enum is limited to be
* int and can't hold more than 32 bits.
*/
enum { RDMA_DEV_FLAGS_LOW(RDMA_BITMAP_ENUM) };
enum { RDMA_DEV_FLAGS_HIGH(RDMA_BITMAP_ENUM) };
static const char * const
rdma_dev_names_low[] = { RDMA_DEV_FLAGS_LOW(RDMA_BITMAP_NAMES) };
static const char * const
rdma_dev_names_high[] = { RDMA_DEV_FLAGS_HIGH(RDMA_BITMAP_NAMES) };
uint32_t high_idx;
#undef RDMA_DEV_FLAGS_LOW
#undef RDMA_DEV_FLAGS_HIGH
if (idx < ARRAY_SIZE(rdma_dev_names_low) && rdma_dev_names_low[idx])
return rdma_dev_names_low[idx];
high_idx = idx - ARRAY_SIZE(rdma_dev_names_low);
if (high_idx < ARRAY_SIZE(rdma_dev_names_high) &&
rdma_dev_names_high[high_idx])
return rdma_dev_names_high[high_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 void dev_print_dim_setting(struct rd *rd, struct nlattr **tb)
{
uint8_t dim_setting;
if (!tb[RDMA_NLDEV_ATTR_DEV_DIM])
return;
dim_setting = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_DEV_DIM]);
if (dim_setting > 1)
return;
print_on_off(rd, "adaptive-moderation", dim_setting);
}
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_udp",
"unspecified" };
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_dim_setting(rd, tb);
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_set_name(struct rd *rd)
{
uint32_t seq;
if (rd_no_arg(rd)) {
pr_err("Please provide device new name.\n");
return -EINVAL;
}
rd_prepare_msg(rd, RDMA_NLDEV_CMD_SET,
&seq, (NLM_F_REQUEST | NLM_F_ACK));
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
mnl_attr_put_strz(rd->nlh, RDMA_NLDEV_ATTR_DEV_NAME, rd_argv(rd));
return rd_sendrecv_msg(rd, seq);
}
static int dev_set_netns(struct rd *rd)
{
char *netns_path;
uint32_t seq;
int netns;
int ret;
if (rd_no_arg(rd)) {
pr_err("Please provide device name.\n");
return -EINVAL;
}
if (asprintf(&netns_path, "%s/%s", NETNS_RUN_DIR, rd_argv(rd)) < 0)
return -ENOMEM;
netns = open(netns_path, O_RDONLY | O_CLOEXEC);
if (netns < 0) {
fprintf(stderr, "Cannot open network namespace \"%s\": %s\n",
rd_argv(rd), strerror(errno));
ret = -EINVAL;
goto done;
}
rd_prepare_msg(rd, RDMA_NLDEV_CMD_SET,
&seq, (NLM_F_REQUEST | NLM_F_ACK));
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_NET_NS_FD, netns);
ret = rd_sendrecv_msg(rd, seq);
close(netns);
done:
free(netns_path);
return ret;
}
static int dev_set_dim_sendmsg(struct rd *rd, uint8_t dim_setting)
{
uint32_t seq;
rd_prepare_msg(rd, RDMA_NLDEV_CMD_SET, &seq,
(NLM_F_REQUEST | NLM_F_ACK));
mnl_attr_put_u32(rd->nlh, RDMA_NLDEV_ATTR_DEV_INDEX, rd->dev_idx);
mnl_attr_put_u8(rd->nlh, RDMA_NLDEV_ATTR_DEV_DIM, dim_setting);
return rd_sendrecv_msg(rd, seq);
}
static int dev_set_dim_off(struct rd *rd)
{
return dev_set_dim_sendmsg(rd, 0);
}
static int dev_set_dim_on(struct rd *rd)
{
return dev_set_dim_sendmsg(rd, 1);
}
static int dev_set_dim(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, dev_help},
{ "on", dev_set_dim_on},
{ "off", dev_set_dim_off},
{ 0 }
};
return rd_exec_cmd(rd, cmds, "parameter");
}
static int dev_one_set(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, dev_help},
{ "name", dev_set_name},
{ "netns", dev_set_netns},
{ "adaptive-moderation", dev_set_dim},
{ 0 }
};
return rd_exec_cmd(rd, cmds, "parameter");
}
static int dev_show(struct rd *rd)
{
return rd_exec_dev(rd, dev_one_show);
}
static int dev_set(struct rd *rd)
{
return rd_exec_require_dev(rd, dev_one_set);
}
int cmd_dev(struct rd *rd)
{
const struct rd_cmd cmds[] = {
{ NULL, dev_show },
{ "show", dev_show },
{ "list", dev_show },
{ "set", dev_set },
{ "help", dev_help },
{ 0 }
};
return rd_exec_cmd(rd, cmds, "dev command");
}