0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033 #include <linux/kernel.h>
0034 #include <linux/ethtool.h>
0035 #include <linux/netdevice.h>
0036
0037 #include "ipoib.h"
0038
0039 struct ipoib_stats {
0040 char stat_string[ETH_GSTRING_LEN];
0041 int stat_offset;
0042 };
0043
0044 #define IPOIB_NETDEV_STAT(m) { \
0045 .stat_string = #m, \
0046 .stat_offset = offsetof(struct rtnl_link_stats64, m) }
0047
0048 static const struct ipoib_stats ipoib_gstrings_stats[] = {
0049 IPOIB_NETDEV_STAT(rx_packets),
0050 IPOIB_NETDEV_STAT(tx_packets),
0051 IPOIB_NETDEV_STAT(rx_bytes),
0052 IPOIB_NETDEV_STAT(tx_bytes),
0053 IPOIB_NETDEV_STAT(tx_errors),
0054 IPOIB_NETDEV_STAT(rx_dropped),
0055 IPOIB_NETDEV_STAT(tx_dropped),
0056 IPOIB_NETDEV_STAT(multicast),
0057 };
0058
0059 #define IPOIB_GLOBAL_STATS_LEN ARRAY_SIZE(ipoib_gstrings_stats)
0060
0061 static void ipoib_get_drvinfo(struct net_device *netdev,
0062 struct ethtool_drvinfo *drvinfo)
0063 {
0064 struct ipoib_dev_priv *priv = ipoib_priv(netdev);
0065
0066 ib_get_device_fw_str(priv->ca, drvinfo->fw_version);
0067
0068 strlcpy(drvinfo->bus_info, dev_name(priv->ca->dev.parent),
0069 sizeof(drvinfo->bus_info));
0070
0071 strlcpy(drvinfo->driver, "ib_ipoib", sizeof(drvinfo->driver));
0072 }
0073
0074 static int ipoib_get_coalesce(struct net_device *dev,
0075 struct ethtool_coalesce *coal,
0076 struct kernel_ethtool_coalesce *kernel_coal,
0077 struct netlink_ext_ack *extack)
0078 {
0079 struct ipoib_dev_priv *priv = ipoib_priv(dev);
0080
0081 coal->rx_coalesce_usecs = priv->ethtool.coalesce_usecs;
0082 coal->rx_max_coalesced_frames = priv->ethtool.max_coalesced_frames;
0083
0084 return 0;
0085 }
0086
0087 static int ipoib_set_coalesce(struct net_device *dev,
0088 struct ethtool_coalesce *coal,
0089 struct kernel_ethtool_coalesce *kernel_coal,
0090 struct netlink_ext_ack *extack)
0091 {
0092 struct ipoib_dev_priv *priv = ipoib_priv(dev);
0093 int ret;
0094
0095
0096
0097
0098
0099 if (coal->rx_coalesce_usecs > 0xffff ||
0100 coal->rx_max_coalesced_frames > 0xffff)
0101 return -EINVAL;
0102
0103 ret = rdma_set_cq_moderation(priv->recv_cq,
0104 coal->rx_max_coalesced_frames,
0105 coal->rx_coalesce_usecs);
0106 if (ret && ret != -EOPNOTSUPP) {
0107 ipoib_warn(priv, "failed modifying CQ (%d)\n", ret);
0108 return ret;
0109 }
0110
0111 priv->ethtool.coalesce_usecs = coal->rx_coalesce_usecs;
0112 priv->ethtool.max_coalesced_frames = coal->rx_max_coalesced_frames;
0113
0114 return 0;
0115 }
0116 static void ipoib_get_ethtool_stats(struct net_device *dev,
0117 struct ethtool_stats __always_unused *stats,
0118 u64 *data)
0119 {
0120 int i;
0121 struct net_device_stats *net_stats = &dev->stats;
0122 u8 *p = (u8 *)net_stats;
0123
0124 for (i = 0; i < IPOIB_GLOBAL_STATS_LEN; i++)
0125 data[i] = *(u64 *)(p + ipoib_gstrings_stats[i].stat_offset);
0126
0127 }
0128 static void ipoib_get_strings(struct net_device __always_unused *dev,
0129 u32 stringset, u8 *data)
0130 {
0131 u8 *p = data;
0132 int i;
0133
0134 switch (stringset) {
0135 case ETH_SS_STATS:
0136 for (i = 0; i < IPOIB_GLOBAL_STATS_LEN; i++) {
0137 memcpy(p, ipoib_gstrings_stats[i].stat_string,
0138 ETH_GSTRING_LEN);
0139 p += ETH_GSTRING_LEN;
0140 }
0141 break;
0142 default:
0143 break;
0144 }
0145 }
0146 static int ipoib_get_sset_count(struct net_device __always_unused *dev,
0147 int sset)
0148 {
0149 switch (sset) {
0150 case ETH_SS_STATS:
0151 return IPOIB_GLOBAL_STATS_LEN;
0152 default:
0153 break;
0154 }
0155 return -EOPNOTSUPP;
0156 }
0157
0158
0159 static inline int ib_speed_enum_to_int(int speed)
0160 {
0161 switch (speed) {
0162 case IB_SPEED_SDR:
0163 return SPEED_2500;
0164 case IB_SPEED_DDR:
0165 return SPEED_5000;
0166 case IB_SPEED_QDR:
0167 case IB_SPEED_FDR10:
0168 return SPEED_10000;
0169 case IB_SPEED_FDR:
0170 return SPEED_14000;
0171 case IB_SPEED_EDR:
0172 return SPEED_25000;
0173 case IB_SPEED_HDR:
0174 return SPEED_50000;
0175 case IB_SPEED_NDR:
0176 return SPEED_100000;
0177 }
0178
0179 return SPEED_UNKNOWN;
0180 }
0181
0182 static int ipoib_get_link_ksettings(struct net_device *netdev,
0183 struct ethtool_link_ksettings *cmd)
0184 {
0185 struct ipoib_dev_priv *priv = ipoib_priv(netdev);
0186 struct ib_port_attr attr;
0187 int ret, speed, width;
0188
0189 if (!netif_carrier_ok(netdev)) {
0190 cmd->base.speed = SPEED_UNKNOWN;
0191 cmd->base.duplex = DUPLEX_UNKNOWN;
0192 return 0;
0193 }
0194
0195 ret = ib_query_port(priv->ca, priv->port, &attr);
0196 if (ret < 0)
0197 return -EINVAL;
0198
0199 speed = ib_speed_enum_to_int(attr.active_speed);
0200 width = ib_width_enum_to_int(attr.active_width);
0201
0202 if (speed < 0 || width < 0)
0203 return -EINVAL;
0204
0205
0206
0207
0208
0209 cmd->base.speed = speed * width;
0210 cmd->base.duplex = DUPLEX_FULL;
0211
0212 cmd->base.phy_address = 0xFF;
0213
0214 cmd->base.autoneg = AUTONEG_ENABLE;
0215 cmd->base.port = PORT_OTHER;
0216
0217 return 0;
0218 }
0219
0220 static const struct ethtool_ops ipoib_ethtool_ops = {
0221 .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
0222 ETHTOOL_COALESCE_RX_MAX_FRAMES,
0223 .get_link_ksettings = ipoib_get_link_ksettings,
0224 .get_drvinfo = ipoib_get_drvinfo,
0225 .get_coalesce = ipoib_get_coalesce,
0226 .set_coalesce = ipoib_set_coalesce,
0227 .get_strings = ipoib_get_strings,
0228 .get_ethtool_stats = ipoib_get_ethtool_stats,
0229 .get_sset_count = ipoib_get_sset_count,
0230 .get_link = ethtool_op_get_link,
0231 };
0232
0233 void ipoib_set_ethtool_ops(struct net_device *dev)
0234 {
0235 dev->ethtool_ops = &ipoib_ethtool_ops;
0236 }