diff --git a/Makefile b/Makefile index 1f88f7f5..dbb4a4af 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ WFLAGS += -Wmissing-declarations -Wold-style-definition -Wformat=2 CFLAGS := $(WFLAGS) $(CCOPTS) -I../include $(DEFINES) $(CFLAGS) YACCFLAGS = -d -t -v -SUBDIRS=lib ip tc bridge misc netem genl tipc devlink man +SUBDIRS=lib ip tc bridge misc netem genl tipc devlink rdma man LIBNETLINK=../lib/libnetlink.a ../lib/libutil.a LDLIBS += $(LIBNETLINK) diff --git a/devlink/devlink.c b/devlink/devlink.c index f9bc16c3..5b325ce6 100644 --- a/devlink/devlink.c +++ b/devlink/devlink.c @@ -25,6 +25,7 @@ #include "list.h" #include "mnlg.h" #include "json_writer.h" +#include "utils.h" #define ESWITCH_MODE_LEGACY "legacy" #define ESWITCH_MODE_SWITCHDEV "switchdev" @@ -160,7 +161,6 @@ static void ifname_map_free(struct ifname_map *ifname_map) free(ifname_map); } -#define BIT(nr) (1UL << (nr)) #define DL_OPT_HANDLE BIT(0) #define DL_OPT_HANDLEP BIT(1) #define DL_OPT_PORT_TYPE BIT(2) @@ -779,7 +779,7 @@ static int dl_argv_parse(struct dl *dl, uint32_t o_required, int err; if (o_required & DL_OPT_HANDLE && o_required & DL_OPT_HANDLEP) { - uint32_t handle_bit = handle_bit; + uint32_t handle_bit; err = dl_argv_handle_both(dl, &opts->bus_name, &opts->dev_name, &opts->port_index, &handle_bit); diff --git a/include/rdma/rdma_netlink.h b/include/rdma/rdma_netlink.h new file mode 100644 index 00000000..861440a8 --- /dev/null +++ b/include/rdma/rdma_netlink.h @@ -0,0 +1,307 @@ +#ifndef _UAPI_RDMA_NETLINK_H +#define _UAPI_RDMA_NETLINK_H + +#include + +enum { + RDMA_NL_RDMA_CM = 1, + RDMA_NL_IWCM, + RDMA_NL_RSVD, + RDMA_NL_LS, /* RDMA Local Services */ + RDMA_NL_NLDEV, /* RDMA device interface */ + RDMA_NL_NUM_CLIENTS +}; + +enum { + RDMA_NL_GROUP_CM = 1, + RDMA_NL_GROUP_IWPM, + RDMA_NL_GROUP_LS, + RDMA_NL_NUM_GROUPS +}; + +#define RDMA_NL_GET_CLIENT(type) ((type & (((1 << 6) - 1) << 10)) >> 10) +#define RDMA_NL_GET_OP(type) (type & ((1 << 10) - 1)) +#define RDMA_NL_GET_TYPE(client, op) ((client << 10) + op) + +enum { + RDMA_NL_RDMA_CM_ID_STATS = 0, + RDMA_NL_RDMA_CM_NUM_OPS +}; + +enum { + RDMA_NL_RDMA_CM_ATTR_SRC_ADDR = 1, + RDMA_NL_RDMA_CM_ATTR_DST_ADDR, + RDMA_NL_RDMA_CM_NUM_ATTR, +}; + +/* iwarp port mapper op-codes */ +enum { + RDMA_NL_IWPM_REG_PID = 0, + RDMA_NL_IWPM_ADD_MAPPING, + RDMA_NL_IWPM_QUERY_MAPPING, + RDMA_NL_IWPM_REMOVE_MAPPING, + RDMA_NL_IWPM_REMOTE_INFO, + RDMA_NL_IWPM_HANDLE_ERR, + RDMA_NL_IWPM_MAPINFO, + RDMA_NL_IWPM_MAPINFO_NUM, + RDMA_NL_IWPM_NUM_OPS +}; + +struct rdma_cm_id_stats { + __u32 qp_num; + __u32 bound_dev_if; + __u32 port_space; + __s32 pid; + __u8 cm_state; + __u8 node_type; + __u8 port_num; + __u8 qp_type; +}; + +enum { + IWPM_NLA_REG_PID_UNSPEC = 0, + IWPM_NLA_REG_PID_SEQ, + IWPM_NLA_REG_IF_NAME, + IWPM_NLA_REG_IBDEV_NAME, + IWPM_NLA_REG_ULIB_NAME, + IWPM_NLA_REG_PID_MAX +}; + +enum { + IWPM_NLA_RREG_PID_UNSPEC = 0, + IWPM_NLA_RREG_PID_SEQ, + IWPM_NLA_RREG_IBDEV_NAME, + IWPM_NLA_RREG_ULIB_NAME, + IWPM_NLA_RREG_ULIB_VER, + IWPM_NLA_RREG_PID_ERR, + IWPM_NLA_RREG_PID_MAX + +}; + +enum { + IWPM_NLA_MANAGE_MAPPING_UNSPEC = 0, + IWPM_NLA_MANAGE_MAPPING_SEQ, + IWPM_NLA_MANAGE_ADDR, + IWPM_NLA_MANAGE_MAPPED_LOC_ADDR, + IWPM_NLA_RMANAGE_MAPPING_ERR, + IWPM_NLA_RMANAGE_MAPPING_MAX +}; + +#define IWPM_NLA_MANAGE_MAPPING_MAX 3 +#define IWPM_NLA_QUERY_MAPPING_MAX 4 +#define IWPM_NLA_MAPINFO_SEND_MAX 3 + +enum { + IWPM_NLA_QUERY_MAPPING_UNSPEC = 0, + IWPM_NLA_QUERY_MAPPING_SEQ, + IWPM_NLA_QUERY_LOCAL_ADDR, + IWPM_NLA_QUERY_REMOTE_ADDR, + IWPM_NLA_RQUERY_MAPPED_LOC_ADDR, + IWPM_NLA_RQUERY_MAPPED_REM_ADDR, + IWPM_NLA_RQUERY_MAPPING_ERR, + IWPM_NLA_RQUERY_MAPPING_MAX +}; + +enum { + IWPM_NLA_MAPINFO_REQ_UNSPEC = 0, + IWPM_NLA_MAPINFO_ULIB_NAME, + IWPM_NLA_MAPINFO_ULIB_VER, + IWPM_NLA_MAPINFO_REQ_MAX +}; + +enum { + IWPM_NLA_MAPINFO_UNSPEC = 0, + IWPM_NLA_MAPINFO_LOCAL_ADDR, + IWPM_NLA_MAPINFO_MAPPED_ADDR, + IWPM_NLA_MAPINFO_MAX +}; + +enum { + IWPM_NLA_MAPINFO_NUM_UNSPEC = 0, + IWPM_NLA_MAPINFO_SEQ, + IWPM_NLA_MAPINFO_SEND_NUM, + IWPM_NLA_MAPINFO_ACK_NUM, + IWPM_NLA_MAPINFO_NUM_MAX +}; + +enum { + IWPM_NLA_ERR_UNSPEC = 0, + IWPM_NLA_ERR_SEQ, + IWPM_NLA_ERR_CODE, + IWPM_NLA_ERR_MAX +}; + +/* + * Local service operations: + * RESOLVE - The client requests the local service to resolve a path. + * SET_TIMEOUT - The local service requests the client to set the timeout. + * IP_RESOLVE - The client requests the local service to resolve an IP to GID. + */ +enum { + RDMA_NL_LS_OP_RESOLVE = 0, + RDMA_NL_LS_OP_SET_TIMEOUT, + RDMA_NL_LS_OP_IP_RESOLVE, + RDMA_NL_LS_NUM_OPS +}; + +/* Local service netlink message flags */ +#define RDMA_NL_LS_F_ERR 0x0100 /* Failed response */ + +/* + * Local service resolve operation family header. + * The layout for the resolve operation: + * nlmsg header + * family header + * attributes + */ + +/* + * Local service path use: + * Specify how the path(s) will be used. + * ALL - For connected CM operation (6 pathrecords) + * UNIDIRECTIONAL - For unidirectional UD (1 pathrecord) + * GMP - For miscellaneous GMP like operation (at least 1 reversible + * pathrecord) + */ +enum { + LS_RESOLVE_PATH_USE_ALL = 0, + LS_RESOLVE_PATH_USE_UNIDIRECTIONAL, + LS_RESOLVE_PATH_USE_GMP, + LS_RESOLVE_PATH_USE_MAX +}; + +#define LS_DEVICE_NAME_MAX 64 + +struct rdma_ls_resolve_header { + __u8 device_name[LS_DEVICE_NAME_MAX]; + __u8 port_num; + __u8 path_use; +}; + +struct rdma_ls_ip_resolve_header { + __u32 ifindex; +}; + +/* Local service attribute type */ +#define RDMA_NLA_F_MANDATORY (1 << 13) +#define RDMA_NLA_TYPE_MASK (~(NLA_F_NESTED | NLA_F_NET_BYTEORDER | \ + RDMA_NLA_F_MANDATORY)) + +/* + * Local service attributes: + * Attr Name Size Byte order + * ----------------------------------------------------- + * PATH_RECORD struct ib_path_rec_data + * TIMEOUT u32 cpu + * SERVICE_ID u64 cpu + * DGID u8[16] BE + * SGID u8[16] BE + * TCLASS u8 + * PKEY u16 cpu + * QOS_CLASS u16 cpu + * IPV4 u32 BE + * IPV6 u8[16] BE + */ +enum { + LS_NLA_TYPE_UNSPEC = 0, + LS_NLA_TYPE_PATH_RECORD, + LS_NLA_TYPE_TIMEOUT, + LS_NLA_TYPE_SERVICE_ID, + LS_NLA_TYPE_DGID, + LS_NLA_TYPE_SGID, + LS_NLA_TYPE_TCLASS, + LS_NLA_TYPE_PKEY, + LS_NLA_TYPE_QOS_CLASS, + LS_NLA_TYPE_IPV4, + LS_NLA_TYPE_IPV6, + LS_NLA_TYPE_MAX +}; + +/* Local service DGID/SGID attribute: big endian */ +struct rdma_nla_ls_gid { + __u8 gid[16]; +}; + +enum rdma_nldev_command { + RDMA_NLDEV_CMD_UNSPEC, + + RDMA_NLDEV_CMD_GET, /* can dump */ + RDMA_NLDEV_CMD_SET, + RDMA_NLDEV_CMD_NEW, + RDMA_NLDEV_CMD_DEL, + + RDMA_NLDEV_CMD_PORT_GET, /* can dump */ + RDMA_NLDEV_CMD_PORT_SET, + RDMA_NLDEV_CMD_PORT_NEW, + RDMA_NLDEV_CMD_PORT_DEL, + + RDMA_NLDEV_NUM_OPS +}; + +enum rdma_nldev_attr { + /* don't change the order or add anything between, this is ABI! */ + RDMA_NLDEV_ATTR_UNSPEC, + + /* Identifier for ib_device */ + RDMA_NLDEV_ATTR_DEV_INDEX, /* u32 */ + + RDMA_NLDEV_ATTR_DEV_NAME, /* string */ + /* + * Device index together with port index are identifiers + * for port/link properties. + * + * For RDMA_NLDEV_CMD_GET commamnd, port index will return number + * of available ports in ib_device, while for port specific operations, + * it will be real port index as it appears in sysfs. Port index follows + * sysfs notation and starts from 1 for the first port. + */ + RDMA_NLDEV_ATTR_PORT_INDEX, /* u32 */ + + /* + * Device and port capabilities + */ + RDMA_NLDEV_ATTR_CAP_FLAGS, /* u64 */ + + /* + * FW version + */ + RDMA_NLDEV_ATTR_FW_VERSION, /* string */ + + /* + * Node GUID (in host byte order) associated with the RDMA device. + */ + RDMA_NLDEV_ATTR_NODE_GUID, /* u64 */ + + /* + * System image GUID (in host byte order) associated with + * this RDMA device and other devices which are part of a + * single system. + */ + RDMA_NLDEV_ATTR_SYS_IMAGE_GUID, /* u64 */ + + /* + * Subnet prefix (in host byte order) + */ + RDMA_NLDEV_ATTR_SUBNET_PREFIX, /* u64 */ + + /* + * Local Identifier (LID), + * According to IB specification, It is 16-bit address assigned + * by the Subnet Manager. Extended to be 32-bit for OmniPath users. + */ + RDMA_NLDEV_ATTR_LID, /* u32 */ + RDMA_NLDEV_ATTR_SM_LID, /* u32 */ + + /* + * LID mask control (LMC) + */ + RDMA_NLDEV_ATTR_LMC, /* u8 */ + + RDMA_NLDEV_ATTR_PORT_STATE, /* u8 */ + RDMA_NLDEV_ATTR_PORT_PHYS_STATE, /* u8 */ + + RDMA_NLDEV_ATTR_DEV_NODE_TYPE, /* u8 */ + + RDMA_NLDEV_ATTR_MAX +}; +#endif /* _UAPI_RDMA_NETLINK_H */ diff --git a/include/utils.h b/include/utils.h index 565bda60..1bb6d6a2 100644 --- a/include/utils.h +++ b/include/utils.h @@ -196,6 +196,8 @@ static inline void __jiffies_to_tv(struct timeval *tv, unsigned long jiffies) int print_timestamp(FILE *fp); void print_nlmsg_timestamp(FILE *fp, const struct nlmsghdr *n); +#define BIT(nr) (1UL << (nr)) + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define BUILD_BUG_ON(cond) ((void)sizeof(char[1 - 2 * !!(cond)])) diff --git a/ip/ipntable.c b/ip/ipntable.c index 879626ee..1837909f 100644 --- a/ip/ipntable.c +++ b/ip/ipntable.c @@ -202,8 +202,6 @@ static int ipntable_modify(int cmd, int flags, int argc, char **argv) if (get_u32(&queue, *argv, 0)) invarg("\"queue\" value is invalid", *argv); - if (!parms_rta) - parms_rta = (struct rtattr *)&parms_buf; rta_addattr32(parms_rta, sizeof(parms_buf), NDTPA_QUEUE_LEN, queue); parms_change = 1; diff --git a/ip/iproute.c b/ip/iproute.c index be9181a6..621b5ca9 100644 --- a/ip/iproute.c +++ b/ip/iproute.c @@ -624,7 +624,7 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) } if (tb[RTA_MULTIPATH]) { struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); - int first = 0; + int first = 1; len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); @@ -634,10 +634,12 @@ int print_route(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) if (nh->rtnh_len > len) break; if (r->rtm_flags&RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) { - if (first) + if (first) { fprintf(fp, "Oifs: "); - else + first = 0; + } else { fprintf(fp, " "); + } } else fprintf(fp, "%s\tnexthop ", _SL_); if (nh->rtnh_len > sizeof(*nh)) { diff --git a/lib/rt_names.c b/lib/rt_names.c index 04c15ff5..e5efd78e 100644 --- a/lib/rt_names.c +++ b/lib/rt_names.c @@ -410,10 +410,6 @@ const char *rtnl_rttable_n2a(__u32 id, char *buf, int len) { struct rtnl_hash_entry *entry; - if (id > RT_TABLE_MAX) { - snprintf(buf, len, "%u", id); - return buf; - } if (!rtnl_rttable_init) rtnl_rttable_initialize(); entry = rtnl_rttable_hash[id & 255]; diff --git a/man/man8/rdma-dev.8 b/man/man8/rdma-dev.8 new file mode 100644 index 00000000..461681b6 --- /dev/null +++ b/man/man8/rdma-dev.8 @@ -0,0 +1,55 @@ +.TH RDMA\-DEV 8 "06 Jul 2017" "iproute2" "Linux" +.SH NAME +rdmak-dev \- RDMA device configuration +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B rdma +.RI "[ " OPTIONS " ]" +.B dev +.RI " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-d\fR[\fIetails\fR] } + +.ti -8 +.B rdma dev show +.RI "[ " DEV " ]" + +.ti -8 +.B rdma dev help + +.SH "DESCRIPTION" +.SS rdma dev show - display rdma device attributes + +.PP +.I "DEV" +- specifies the RDMA device to show. +If this argument is omitted all devices are listed. + +.SH "EXAMPLES" +.PP +rdma dev +.RS 4 +Shows the state of all RDMA devices on the system. +.RE +.PP +rdma dev show mlx5_3 +.RS 4 +Shows the state of specified RDMA device. +.RE +.PP + +.SH SEE ALSO +.BR rdma (8), +.BR rdma-link (8), +.br + +.SH AUTHOR +Leon Romanovsky diff --git a/man/man8/rdma-link.8 b/man/man8/rdma-link.8 new file mode 100644 index 00000000..8ed049ef --- /dev/null +++ b/man/man8/rdma-link.8 @@ -0,0 +1,55 @@ +.TH RDMA\-LINK 8 "06 Jul 2017" "iproute2" "Linux" +.SH NAME +rdma-link \- rdma link configuration +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B devlink +.RI "[ " OPTIONS " ]" +.B link +.RI " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-d\fR[\fIetails\fR] } + +.ti -8 +.B rdma link show +.RI "[ " DEV/PORT_INDEX " ]" + +.ti -8 +.B rdma link help + +.SH "DESCRIPTION" +.SS rdma link show - display rdma link attributes + +.PP +.I "DEV/PORT_INDEX" +- specifies the RDMa link to show. +If this argument is omitted all links are listed. + +.SH "EXAMPLES" +.PP +rdma link show +.RS 4 +Shows the state of all rdma links on the system. +.RE +.PP +rdma link show mlx5_2/1 +.RS 4 +Shows the state of specified rdma link. +.RE +.PP + +.SH SEE ALSO +.BR rdma (8), +.BR rdma-dev (8), +.br + +.SH AUTHOR +Leon Romanovsky diff --git a/man/man8/rdma.8 b/man/man8/rdma.8 new file mode 100644 index 00000000..798b33d3 --- /dev/null +++ b/man/man8/rdma.8 @@ -0,0 +1,102 @@ +.TH RDMA 8 "28 Mar 2017" "iproute2" "Linux" +.SH NAME +rdma \- RDMA tool +.SH SYNOPSIS +.sp +.ad l +.in +8 +.ti -8 +.B rdma +.RI "[ " OPTIONS " ] " OBJECT " { " COMMAND " | " +.BR help " }" +.sp + +.ti -8 +.IR OBJECT " := { " +.BR dev " | " link " }" +.sp + +.ti -8 +.IR OPTIONS " := { " +\fB\-V\fR[\fIersion\fR] | +\fB\-d\fR[\fIetails\fR] } +\fB\-j\fR[\fIson\fR] } +\fB\-p\fR[\fIretty\fR] } + +.SH OPTIONS + +.TP +.BR "\-V" , " -Version" +Print the version of the +.B rdma +tool and exit. + +.TP +.BR "\-d" , " --details" +Otuput detailed information. + +.TP +.BR "\-p" , " --pretty" +When combined with -j generate a pretty JSON output. + +.TP +.BR "\-j" , " --json" +Generate JSON output. + +.SS +.I OBJECT + +.TP +.B dev +- RDMA device. + +.TP +.B link +- RDMA port related. + +.PP +The names of all objects may be written in full or +abbreviated form, for example +.B stats +can be abbreviated as +.B stat +or just +.B s. + +.SS +.I COMMAND + +Specifies the action to perform on the object. +The set of possible actions depends on the object type. +As a rule, it is possible to +.B show +(or +.B list +) objects, but some objects do not allow all of these operations +or have some additional commands. The +.B help +command is available for all objects. It prints +out a list of available commands and argument syntax conventions. +.sp +If no command is given, some default command is assumed. +Usually it is +.B list +or, if the objects of this class cannot be listed, +.BR "help" . + +.SH EXIT STATUS +Exit status is 0 if command was successful or a positive integer upon failure. + +.SH SEE ALSO +.BR rdma-dev (8), +.BR rdma-link (8), +.br + +.SH REPORTING BUGS +Report any bugs to the Linux RDMA mailing list +.B +where the development and maintenance is primarily done. +You do not have to be subscribed to the list to send a message there. + +.SH AUTHOR +Leon Romanovsky diff --git a/misc/ss.c b/misc/ss.c index 10360e5a..bd68ecdc 100644 --- a/misc/ss.c +++ b/misc/ss.c @@ -1440,7 +1440,6 @@ static int remember_he(struct aafilter *a, struct hostent *he) if ((b = malloc(sizeof(*b))) == NULL) return cnt; *b = *a; - b->next = a->next; a->next = b; } memcpy(b->addr.data, *ptr, len); @@ -1671,7 +1670,7 @@ void *parse_hostcond(char *addr, bool is_port) } } } - if (!is_port && addr && *addr && *addr != '*') { + if (!is_port && *addr && *addr != '*') { if (get_prefix_1(&a.addr, addr, fam)) { if (get_dns_host(&a, addr, fam)) { fprintf(stderr, "Error: an inet prefix is expected rather than \"%s\".\n", addr); diff --git a/rdma/.gitignore b/rdma/.gitignore new file mode 100644 index 00000000..51fb172b --- /dev/null +++ b/rdma/.gitignore @@ -0,0 +1 @@ +rdma diff --git a/rdma/Makefile b/rdma/Makefile new file mode 100644 index 00000000..1a9e4b1a --- /dev/null +++ b/rdma/Makefile @@ -0,0 +1,22 @@ +include ../Config + +ifeq ($(HAVE_MNL),y) + +RDMA_OBJ = rdma.o utils.o dev.o link.o + +TARGETS=rdma +CFLAGS += $(shell $(PKG_CONFIG) libmnl --cflags) +LDLIBS += $(shell $(PKG_CONFIG) libmnl --libs) + +endif + +all: $(TARGETS) $(LIBS) + +rdma: $(RDMA_OBJ) $(LIBS) + $(QUIET_LINK)$(CC) $^ $(LDFLAGS) $(LDLIBS) -o $@ + +install: all + install -m 0755 $(TARGETS) $(DESTDIR)$(SBINDIR) + +clean: + rm -f $(RDMA_OBJ) $(TARGETS) diff --git a/rdma/dev.c b/rdma/dev.c new file mode 100644 index 00000000..9fadf3ac --- /dev/null +++ b/rdma/dev.c @@ -0,0 +1,284 @@ +/* + * 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"); +} diff --git a/rdma/link.c b/rdma/link.c new file mode 100644 index 00000000..eae96cd8 --- /dev/null +++ b/rdma/link.c @@ -0,0 +1,343 @@ +/* + * link.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 link_help(struct rd *rd) +{ + pr_out("Usage: %s link show [DEV/PORT_INDEX]\n", rd->filename); + return 0; +} + +static const char *caps_to_str(uint32_t idx) +{ +#define RDMA_PORT_FLAGS(x) \ + x(SM, 1) \ + x(NOTICE, 2) \ + x(TRAP, 3) \ + x(OPT_IPD, 4) \ + x(AUTO_MIGR, 5) \ + x(SL_MAP, 6) \ + x(MKEY_NVRAM, 7) \ + x(PKEY_NVRAM, 8) \ + x(LED_INFO, 9) \ + x(SM_DISABLED, 10) \ + x(SYS_IMAGE_GUIG, 11) \ + x(PKEY_SW_EXT_PORT_TRAP, 12) \ + x(EXTENDED_SPEEDS, 14) \ + x(CM, 16) \ + x(SNMP_TUNNEL, 17) \ + x(REINIT, 18) \ + x(DEVICE_MGMT, 19) \ + x(VENDOR_CLASS, 20) \ + x(DR_NOTICE, 21) \ + x(CAP_MASK_NOTICE, 22) \ + x(BOOT_MGMT, 23) \ + x(LINK_LATENCY, 24) \ + x(CLIENT_REG, 23) \ + x(IP_BASED_GIDS, 26) + + enum { RDMA_PORT_FLAGS(RDMA_BITMAP_ENUM) }; + + static const char * const + rdma_port_names[] = { RDMA_PORT_FLAGS(RDMA_BITMAP_NAMES) }; + #undef RDMA_PORT_FLAGS + + if (idx < ARRAY_SIZE(rdma_port_names) && rdma_port_names[idx]) + return rdma_port_names[idx]; + return "UNKNOWN"; +} + +static void link_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, caps_to_str(idx)); + } else { + pr_out("%s", 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 link_print_subnet_prefix(struct rd *rd, struct nlattr **tb) +{ + uint64_t subnet_prefix; + uint16_t vp[4]; + char str[32]; + + if (!tb[RDMA_NLDEV_ATTR_SUBNET_PREFIX]) + return; + + subnet_prefix = mnl_attr_get_u64(tb[RDMA_NLDEV_ATTR_SUBNET_PREFIX]); + memcpy(vp, &subnet_prefix, 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, "subnet_prefix", str); + else + pr_out("subnet_prefix %s ", str); +} + +static void link_print_lid(struct rd *rd, struct nlattr **tb) +{ + uint32_t lid; + + if (!tb[RDMA_NLDEV_ATTR_LID]) + return; + + lid = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_LID]); + if (rd->json_output) + jsonw_uint_field(rd->jw, "lid", lid); + else + pr_out("lid %u ", lid); +} + +static void link_print_sm_lid(struct rd *rd, struct nlattr **tb) +{ + uint32_t sm_lid; + + if (!tb[RDMA_NLDEV_ATTR_SM_LID]) + return; + + sm_lid = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_SM_LID]); + if (rd->json_output) + jsonw_uint_field(rd->jw, "sm_lid", sm_lid); + else + pr_out("sm_lid %u ", sm_lid); +} + +static void link_print_lmc(struct rd *rd, struct nlattr **tb) +{ + uint8_t lmc; + + if (!tb[RDMA_NLDEV_ATTR_LMC]) + return; + + lmc = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_LMC]); + if (rd->json_output) + jsonw_uint_field(rd->jw, "lmc", lmc); + else + pr_out("lmc %u ", lmc); +} + +static const char *link_state_to_str(uint8_t link_state) +{ + static const char * const link_state_str[] = { "NOP", "DOWN", + "INIT", "ARMED", + "ACTIVE", + "ACTIVE_DEFER" }; + if (link_state < ARRAY_SIZE(link_state_str)) + return link_state_str[link_state]; + return "UNKNOWN"; +} + +static void link_print_state(struct rd *rd, struct nlattr **tb) +{ + uint8_t state; + + if (!tb[RDMA_NLDEV_ATTR_PORT_STATE]) + return; + + state = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_PORT_STATE]); + if (rd->json_output) + jsonw_string_field(rd->jw, "state", link_state_to_str(state)); + else + pr_out("state %s ", link_state_to_str(state)); +} + +static const char *phys_state_to_str(uint8_t phys_state) +{ + static const char * const phys_state_str[] = { "NOP", "SLEEP", + "POLLING", "DISABLED", + "ARMED", "LINK_UP", + "LINK_ERROR_RECOVER", + "PHY_TEST", "UNKNOWN", + "OPA_OFFLINE", + "UNKNOWN", "OPA_TEST" }; + if (phys_state < ARRAY_SIZE(phys_state_str)) + return phys_state_str[phys_state]; + return "UNKNOWN"; +}; + +static void link_print_phys_state(struct rd *rd, struct nlattr **tb) +{ + uint8_t phys_state; + + if (!tb[RDMA_NLDEV_ATTR_PORT_PHYS_STATE]) + return; + + phys_state = mnl_attr_get_u8(tb[RDMA_NLDEV_ATTR_PORT_PHYS_STATE]); + if (rd->json_output) + jsonw_string_field(rd->jw, "physical_state", + phys_state_to_str(phys_state)); + else + pr_out("physical_state %s ", phys_state_to_str(phys_state)); +} + +static int link_parse_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct rd *rd = data; + uint32_t port, idx; + char name[32]; + + 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; + + if (!tb[RDMA_NLDEV_ATTR_PORT_INDEX]) { + pr_err("This tool doesn't support switches yet\n"); + return MNL_CB_ERROR; + } + + idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + port = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); + snprintf(name, 32, "%s/%u", + mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]), port); + + if (rd->json_output) { + jsonw_uint_field(rd->jw, "ifindex", idx); + jsonw_uint_field(rd->jw, "port", port); + jsonw_string_field(rd->jw, "ifname", name); + + } else { + pr_out("%u/%u: %s: ", idx, port, name); + } + + link_print_subnet_prefix(rd, tb); + link_print_lid(rd, tb); + link_print_sm_lid(rd, tb); + link_print_lmc(rd, tb); + link_print_state(rd, tb); + link_print_phys_state(rd, tb); + if (rd->show_details) + link_print_caps(rd, tb); + + if (!rd->json_output) + pr_out("\n"); + return MNL_CB_OK; +} + +static int link_no_args(struct rd *rd) +{ + uint32_t seq; + int ret; + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_PORT_GET, &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_ATTR_PORT_INDEX, rd->port_idx); + ret = rd_send_msg(rd); + if (ret) + return ret; + + if (rd->json_output) + jsonw_start_object(rd->jw); + ret = rd_recv_msg(rd, link_parse_cb, rd, seq); + if (rd->json_output) + jsonw_end_object(rd->jw); + return ret; +} + +static int link_one_show(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, link_no_args}, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "parameter"); +} + +static int link_show(struct rd *rd) +{ + struct dev_map *dev_map; + uint32_t port; + 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; + for (port = 1; port < dev_map->num_ports + 1; port++) { + rd->port_idx = port; + ret = link_one_show(rd); + if (ret) + goto out; + } + } + + } else { + dev_map = dev_map_lookup(rd, true); + port = get_port_from_argv(rd); + if (!dev_map || port > dev_map->num_ports) { + 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; + for (; rd->port_idx < dev_map->num_ports + 1; rd->port_idx++) { + ret = link_one_show(rd); + if (ret) + goto out; + if (port) + /* + * We got request to show link for devname + * with port index. + */ + break; + } + } + +out: + if (rd->json_output) + jsonw_end_array(rd->jw); + return ret; +} + +int cmd_link(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, link_show }, + { "show", link_show }, + { "list", link_show }, + { "help", link_help }, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "link command"); +} diff --git a/rdma/rdma.c b/rdma/rdma.c new file mode 100644 index 00000000..f9f4f2a2 --- /dev/null +++ b/rdma/rdma.c @@ -0,0 +1,143 @@ +/* + * rdma.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 "SNAPSHOT.h" + +static void help(char *name) +{ + pr_out("Usage: %s [ OPTIONS ] OBJECT { COMMAND | help }\n" + "where OBJECT := { dev | link | help }\n" + " OPTIONS := { -V[ersion] | -d[etails] | -j[son] | -p[retty]}\n", name); +} + +static int cmd_help(struct rd *rd) +{ + help(rd->filename); + return 0; +} + +static int rd_cmd(struct rd *rd) +{ + const struct rd_cmd cmds[] = { + { NULL, cmd_help }, + { "help", cmd_help }, + { "dev", cmd_dev }, + { "link", cmd_link }, + { 0 } + }; + + return rd_exec_cmd(rd, cmds, "object"); +} + +static int rd_init(struct rd *rd, int argc, char **argv, char *filename) +{ + uint32_t seq; + int ret; + + rd->filename = filename; + rd->argc = argc; + rd->argv = argv; + INIT_LIST_HEAD(&rd->dev_map_list); + + if (rd->json_output) { + rd->jw = jsonw_new(stdout); + if (!rd->jw) { + pr_err("Failed to create JSON writer\n"); + return -ENOMEM; + } + jsonw_pretty(rd->jw, rd->pretty_output); + } + + rd->buff = malloc(MNL_SOCKET_BUFFER_SIZE); + if (!rd->buff) + return -ENOMEM; + + rd_prepare_msg(rd, RDMA_NLDEV_CMD_GET, + &seq, (NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP)); + ret = rd_send_msg(rd); + if (ret) + return ret; + + return rd_recv_msg(rd, rd_dev_init_cb, rd, seq); +} + +static void rd_free(struct rd *rd) +{ + if (rd->json_output) + jsonw_destroy(&rd->jw); + free(rd->buff); + rd_free_devmap(rd); +} + +int main(int argc, char **argv) +{ + static const struct option long_options[] = { + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + { "json", no_argument, NULL, 'j' }, + { "pretty", no_argument, NULL, 'p' }, + { "details", no_argument, NULL, 'd' }, + { NULL, 0, NULL, 0 } + }; + bool pretty_output = false; + bool show_details = false; + bool json_output = false; + char *filename; + struct rd rd; + int opt; + int err; + + filename = basename(argv[0]); + + while ((opt = getopt_long(argc, argv, "Vhdpj", + long_options, NULL)) >= 0) { + switch (opt) { + case 'V': + printf("%s utility, iproute2-ss%s\n", + filename, SNAPSHOT); + return EXIT_SUCCESS; + case 'p': + pretty_output = true; + break; + case 'd': + show_details = true; + break; + case 'j': + json_output = true; + break; + case 'h': + help(filename); + return EXIT_SUCCESS; + default: + pr_err("Unknown option.\n"); + help(filename); + return EXIT_FAILURE; + } + } + + argc -= optind; + argv += optind; + + rd.show_details = show_details; + rd.json_output = json_output; + rd.pretty_output = pretty_output; + + err = rd_init(&rd, argc, argv, filename); + if (err) + goto out; + + err = rd_cmd(&rd); +out: + /* Always cleanup */ + rd_free(&rd); + return err ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/rdma/rdma.h b/rdma/rdma.h new file mode 100644 index 00000000..d551eb29 --- /dev/null +++ b/rdma/rdma.h @@ -0,0 +1,91 @@ +/* + * rdma.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 + */ +#ifndef _RDMA_TOOL_H_ +#define _RDMA_TOOL_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "list.h" +#include "utils.h" +#include "json_writer.h" + +#define pr_err(args...) fprintf(stderr, ##args) +#define pr_out(args...) fprintf(stdout, ##args) + +#define RDMA_BITMAP_ENUM(name, bit_no) RDMA_BITMAP_##name = BIT(bit_no), +#define RDMA_BITMAP_NAMES(name, bit_no) [bit_no] = #name, + +struct dev_map { + struct list_head list; + char *dev_name; + uint32_t num_ports; + uint32_t idx; +}; + +struct rd { + int argc; + char **argv; + char *filename; + bool show_details; + struct list_head dev_map_list; + uint32_t dev_idx; + uint32_t port_idx; + struct mnl_socket *nl; + struct nlmsghdr *nlh; + char *buff; + json_writer_t *jw; + bool json_output; + bool pretty_output; +}; + +struct rd_cmd { + const char *cmd; + int (*func)(struct rd *rd); +}; + +/* + * Parser interface + */ +bool rd_no_arg(struct rd *rd); +void rd_arg_inc(struct rd *rd); + +char *rd_argv(struct rd *rd); +uint32_t get_port_from_argv(struct rd *rd); + +/* + * Commands interface + */ +int cmd_dev(struct rd *rd); +int cmd_link(struct rd *rd); +int rd_exec_cmd(struct rd *rd, const struct rd_cmd *c, const char *str); + +/* + * Device manipulation + */ +void rd_free_devmap(struct rd *rd); +struct dev_map *dev_map_lookup(struct rd *rd, bool allow_port_index); +struct dev_map *_dev_map_lookup(struct rd *rd, const char *dev_name); + +/* + * Netlink + */ +int rd_send_msg(struct rd *rd); +int rd_recv_msg(struct rd *rd, mnl_cb_t callback, void *data, uint32_t seq); +void rd_prepare_msg(struct rd *rd, uint32_t cmd, uint32_t *seq, uint16_t flags); +int rd_dev_init_cb(const struct nlmsghdr *nlh, void *data); +int rd_attr_cb(const struct nlattr *attr, void *data); +#endif /* _RDMA_TOOL_H_ */ diff --git a/rdma/utils.c b/rdma/utils.c new file mode 100644 index 00000000..eb4377cf --- /dev/null +++ b/rdma/utils.c @@ -0,0 +1,266 @@ +/* + * 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" + +static int rd_argc(struct rd *rd) +{ + return rd->argc; +} + +char *rd_argv(struct rd *rd) +{ + if (!rd_argc(rd)) + return NULL; + return *rd->argv; +} + +static int strcmpx(const char *str1, const char *str2) +{ + if (strlen(str1) > strlen(str2)) + return -1; + return strncmp(str1, str2, strlen(str1)); +} + +static bool rd_argv_match(struct rd *rd, const char *pattern) +{ + if (!rd_argc(rd)) + return false; + return strcmpx(rd_argv(rd), pattern) == 0; +} + +void rd_arg_inc(struct rd *rd) +{ + if (!rd_argc(rd)) + return; + rd->argc--; + rd->argv++; +} + +bool rd_no_arg(struct rd *rd) +{ + return rd_argc(rd) == 0; +} + +uint32_t get_port_from_argv(struct rd *rd) +{ + char *slash; + + slash = strchr(rd_argv(rd), '/'); + /* if no port found, return 0 */ + return slash ? atoi(slash + 1) : 0; +} + +static struct dev_map *dev_map_alloc(const char *dev_name) +{ + struct dev_map *dev_map; + + dev_map = calloc(1, sizeof(*dev_map)); + if (!dev_map) + return NULL; + dev_map->dev_name = strdup(dev_name); + + return dev_map; +} + +static void dev_map_free(struct dev_map *dev_map) +{ + if (!dev_map) + return; + + free(dev_map->dev_name); + free(dev_map); +} + +static void dev_map_cleanup(struct rd *rd) +{ + struct dev_map *dev_map, *tmp; + + list_for_each_entry_safe(dev_map, tmp, + &rd->dev_map_list, list) { + list_del(&dev_map->list); + dev_map_free(dev_map); + } +} + +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, + [RDMA_NLDEV_ATTR_PORT_INDEX] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_CAP_FLAGS] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_FW_VERSION] = MNL_TYPE_NUL_STRING, + [RDMA_NLDEV_ATTR_NODE_GUID] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_SYS_IMAGE_GUID] = MNL_TYPE_U64, + [RDMA_NLDEV_ATTR_LID] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_SM_LID] = MNL_TYPE_U32, + [RDMA_NLDEV_ATTR_LMC] = MNL_TYPE_U8, + [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, +}; + +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) < 0) + return MNL_CB_ERROR; + + type = mnl_attr_get_type(attr); + + if (mnl_attr_validate(attr, nldev_policy[type]) < 0) + return MNL_CB_ERROR; + + tb[type] = attr; + return MNL_CB_OK; +} + +int rd_dev_init_cb(const struct nlmsghdr *nlh, void *data) +{ + struct nlattr *tb[RDMA_NLDEV_ATTR_MAX] = {}; + struct dev_map *dev_map; + struct rd *rd = data; + const char *dev_name; + + mnl_attr_parse(nlh, 0, rd_attr_cb, tb); + if (!tb[RDMA_NLDEV_ATTR_DEV_NAME] || !tb[RDMA_NLDEV_ATTR_DEV_INDEX]) + return MNL_CB_ERROR; + if (!tb[RDMA_NLDEV_ATTR_PORT_INDEX]) { + pr_err("This tool doesn't support switches yet\n"); + return MNL_CB_ERROR; + } + + dev_name = mnl_attr_get_str(tb[RDMA_NLDEV_ATTR_DEV_NAME]); + + dev_map = dev_map_alloc(dev_name); + if (!dev_map) + /* The main function will cleanup the allocations */ + return MNL_CB_ERROR; + list_add_tail(&dev_map->list, &rd->dev_map_list); + + dev_map->num_ports = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_PORT_INDEX]); + dev_map->idx = mnl_attr_get_u32(tb[RDMA_NLDEV_ATTR_DEV_INDEX]); + return MNL_CB_OK; +} + +void rd_free_devmap(struct rd *rd) +{ + if (!rd) + return; + dev_map_cleanup(rd); +} + +int rd_exec_cmd(struct rd *rd, const struct rd_cmd *cmds, const char *str) +{ + const struct rd_cmd *c; + + /* First argument in objs table is default variant */ + if (rd_no_arg(rd)) + return cmds->func(rd); + + for (c = cmds + 1; c->cmd; ++c) { + if (rd_argv_match(rd, c->cmd)) { + /* Move to next argument */ + rd_arg_inc(rd); + return c->func(rd); + } + } + + pr_err("Unknown %s '%s'.\n", str, rd_argv(rd)); + return 0; +} + +void rd_prepare_msg(struct rd *rd, uint32_t cmd, uint32_t *seq, uint16_t flags) +{ + *seq = time(NULL); + + rd->nlh = mnl_nlmsg_put_header(rd->buff); + rd->nlh->nlmsg_type = RDMA_NL_GET_TYPE(RDMA_NL_NLDEV, cmd); + rd->nlh->nlmsg_seq = *seq; + rd->nlh->nlmsg_flags = flags; +} + +int rd_send_msg(struct rd *rd) +{ + int ret; + + rd->nl = mnl_socket_open(NETLINK_RDMA); + if (!rd->nl) { + pr_err("Failed to open NETLINK_RDMA socket\n"); + return -ENODEV; + } + + ret = mnl_socket_bind(rd->nl, 0, MNL_SOCKET_AUTOPID); + if (ret < 0) { + pr_err("Failed to bind socket with err %d\n", ret); + goto err; + } + + ret = mnl_socket_sendto(rd->nl, rd->nlh, rd->nlh->nlmsg_len); + if (ret < 0) { + pr_err("Failed to send to socket with err %d\n", ret); + goto err; + } + return 0; + +err: + mnl_socket_close(rd->nl); + return ret; +} + +int rd_recv_msg(struct rd *rd, mnl_cb_t callback, void *data, unsigned int seq) +{ + int ret; + unsigned int portid; + char buf[MNL_SOCKET_BUFFER_SIZE]; + + portid = mnl_socket_get_portid(rd->nl); + do { + ret = mnl_socket_recvfrom(rd->nl, buf, sizeof(buf)); + if (ret <= 0) + break; + + ret = mnl_cb_run(buf, ret, seq, portid, callback, data); + } while (ret > 0); + + mnl_socket_close(rd->nl); + return ret; +} + +struct dev_map *_dev_map_lookup(struct rd *rd, const char *dev_name) +{ + struct dev_map *dev_map; + + list_for_each_entry(dev_map, &rd->dev_map_list, list) + if (strcmp(dev_name, dev_map->dev_name) == 0) + return dev_map; + + return NULL; +} + +struct dev_map *dev_map_lookup(struct rd *rd, bool allow_port_index) +{ + struct dev_map *dev_map; + char *dev_name; + char *slash; + + dev_name = strdup(rd_argv(rd)); + if (allow_port_index) { + slash = strrchr(dev_name, '/'); + if (slash) + *slash = '\0'; + } + + dev_map = _dev_map_lookup(rd, dev_name); + free(dev_name); + return dev_map; +} diff --git a/tc/m_gact.c b/tc/m_gact.c index 1a258337..df143c9e 100644 --- a/tc/m_gact.c +++ b/tc/m_gact.c @@ -76,7 +76,6 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p, { int argc = *argc_p; char **argv = *argv_p; - int ok = 0; struct tc_gact p = { 0 }; #ifdef CONFIG_GACT_PROB int rd = 0; @@ -89,17 +88,14 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p, if (matches(*argv, "gact") == 0) { - ok++; argc--; argv++; - } else { - if (parse_action_control(&argc, &argv, &p.action, false) == -1) - usage(); - ok++; + } else if (parse_action_control(&argc, &argv, &p.action, false) == -1) { + usage(); /* does not return */ } #ifdef CONFIG_GACT_PROB - if (ok && argc > 0) { + if (argc > 0) { if (matches(*argv, "random") == 0) { rd = 1; NEXT_ARG(); @@ -142,15 +138,11 @@ parse_gact(struct action_util *a, int *argc_p, char ***argv_p, } argc--; argv++; - ok++; } else if (matches(*argv, "help") == 0) { usage(); } } - if (!ok) - return -1; - tail = NLMSG_TAIL(n); addattr_l(n, MAX_MSG, tca_id, NULL, 0); addattr_l(n, MAX_MSG, TCA_GACT_PARMS, &p, sizeof(p));