1 /** 2 * Copyright 2013 Cisco Systems, Inc. All rights reserved. 3 * 4 * This program is free software; you may redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; version 2 of the License. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 9 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 10 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 11 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 12 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 13 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 15 * SOFTWARE. 16 * 17 */ 18 19 #include <linux/netdevice.h> 20 #include <linux/ethtool.h> 21 22 #include "enic_res.h" 23 #include "enic.h" 24 #include "enic_dev.h" 25 26 struct enic_stat { 27 char name[ETH_GSTRING_LEN]; 28 unsigned int index; 29 }; 30 31 #define ENIC_TX_STAT(stat) { \ 32 .name = #stat, \ 33 .index = offsetof(struct vnic_tx_stats, stat) / sizeof(u64) \ 34 } 35 36 #define ENIC_RX_STAT(stat) { \ 37 .name = #stat, \ 38 .index = offsetof(struct vnic_rx_stats, stat) / sizeof(u64) \ 39 } 40 41 static const struct enic_stat enic_tx_stats[] = { 42 ENIC_TX_STAT(tx_frames_ok), 43 ENIC_TX_STAT(tx_unicast_frames_ok), 44 ENIC_TX_STAT(tx_multicast_frames_ok), 45 ENIC_TX_STAT(tx_broadcast_frames_ok), 46 ENIC_TX_STAT(tx_bytes_ok), 47 ENIC_TX_STAT(tx_unicast_bytes_ok), 48 ENIC_TX_STAT(tx_multicast_bytes_ok), 49 ENIC_TX_STAT(tx_broadcast_bytes_ok), 50 ENIC_TX_STAT(tx_drops), 51 ENIC_TX_STAT(tx_errors), 52 ENIC_TX_STAT(tx_tso), 53 }; 54 55 static const struct enic_stat enic_rx_stats[] = { 56 ENIC_RX_STAT(rx_frames_ok), 57 ENIC_RX_STAT(rx_frames_total), 58 ENIC_RX_STAT(rx_unicast_frames_ok), 59 ENIC_RX_STAT(rx_multicast_frames_ok), 60 ENIC_RX_STAT(rx_broadcast_frames_ok), 61 ENIC_RX_STAT(rx_bytes_ok), 62 ENIC_RX_STAT(rx_unicast_bytes_ok), 63 ENIC_RX_STAT(rx_multicast_bytes_ok), 64 ENIC_RX_STAT(rx_broadcast_bytes_ok), 65 ENIC_RX_STAT(rx_drop), 66 ENIC_RX_STAT(rx_no_bufs), 67 ENIC_RX_STAT(rx_errors), 68 ENIC_RX_STAT(rx_rss), 69 ENIC_RX_STAT(rx_crc_errors), 70 ENIC_RX_STAT(rx_frames_64), 71 ENIC_RX_STAT(rx_frames_127), 72 ENIC_RX_STAT(rx_frames_255), 73 ENIC_RX_STAT(rx_frames_511), 74 ENIC_RX_STAT(rx_frames_1023), 75 ENIC_RX_STAT(rx_frames_1518), 76 ENIC_RX_STAT(rx_frames_to_max), 77 }; 78 79 static const unsigned int enic_n_tx_stats = ARRAY_SIZE(enic_tx_stats); 80 static const unsigned int enic_n_rx_stats = ARRAY_SIZE(enic_rx_stats); 81 82 void enic_intr_coal_set_rx(struct enic *enic, u32 timer) 83 { 84 int i; 85 int intr; 86 87 for (i = 0; i < enic->rq_count; i++) { 88 intr = enic_msix_rq_intr(enic, i); 89 vnic_intr_coalescing_timer_set(&enic->intr[intr], timer); 90 } 91 } 92 93 static int enic_get_settings(struct net_device *netdev, 94 struct ethtool_cmd *ecmd) 95 { 96 struct enic *enic = netdev_priv(netdev); 97 98 ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE); 99 ecmd->advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE); 100 ecmd->port = PORT_FIBRE; 101 ecmd->transceiver = XCVR_EXTERNAL; 102 103 if (netif_carrier_ok(netdev)) { 104 ethtool_cmd_speed_set(ecmd, vnic_dev_port_speed(enic->vdev)); 105 ecmd->duplex = DUPLEX_FULL; 106 } else { 107 ethtool_cmd_speed_set(ecmd, -1); 108 ecmd->duplex = -1; 109 } 110 111 ecmd->autoneg = AUTONEG_DISABLE; 112 113 return 0; 114 } 115 116 static void enic_get_drvinfo(struct net_device *netdev, 117 struct ethtool_drvinfo *drvinfo) 118 { 119 struct enic *enic = netdev_priv(netdev); 120 struct vnic_devcmd_fw_info *fw_info; 121 122 enic_dev_fw_info(enic, &fw_info); 123 124 strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver)); 125 strlcpy(drvinfo->version, DRV_VERSION, sizeof(drvinfo->version)); 126 strlcpy(drvinfo->fw_version, fw_info->fw_version, 127 sizeof(drvinfo->fw_version)); 128 strlcpy(drvinfo->bus_info, pci_name(enic->pdev), 129 sizeof(drvinfo->bus_info)); 130 } 131 132 static void enic_get_strings(struct net_device *netdev, u32 stringset, 133 u8 *data) 134 { 135 unsigned int i; 136 137 switch (stringset) { 138 case ETH_SS_STATS: 139 for (i = 0; i < enic_n_tx_stats; i++) { 140 memcpy(data, enic_tx_stats[i].name, ETH_GSTRING_LEN); 141 data += ETH_GSTRING_LEN; 142 } 143 for (i = 0; i < enic_n_rx_stats; i++) { 144 memcpy(data, enic_rx_stats[i].name, ETH_GSTRING_LEN); 145 data += ETH_GSTRING_LEN; 146 } 147 break; 148 } 149 } 150 151 static int enic_get_sset_count(struct net_device *netdev, int sset) 152 { 153 switch (sset) { 154 case ETH_SS_STATS: 155 return enic_n_tx_stats + enic_n_rx_stats; 156 default: 157 return -EOPNOTSUPP; 158 } 159 } 160 161 static void enic_get_ethtool_stats(struct net_device *netdev, 162 struct ethtool_stats *stats, u64 *data) 163 { 164 struct enic *enic = netdev_priv(netdev); 165 struct vnic_stats *vstats; 166 unsigned int i; 167 168 enic_dev_stats_dump(enic, &vstats); 169 170 for (i = 0; i < enic_n_tx_stats; i++) 171 *(data++) = ((u64 *)&vstats->tx)[enic_tx_stats[i].index]; 172 for (i = 0; i < enic_n_rx_stats; i++) 173 *(data++) = ((u64 *)&vstats->rx)[enic_rx_stats[i].index]; 174 } 175 176 static u32 enic_get_msglevel(struct net_device *netdev) 177 { 178 struct enic *enic = netdev_priv(netdev); 179 return enic->msg_enable; 180 } 181 182 static void enic_set_msglevel(struct net_device *netdev, u32 value) 183 { 184 struct enic *enic = netdev_priv(netdev); 185 enic->msg_enable = value; 186 } 187 188 static int enic_get_coalesce(struct net_device *netdev, 189 struct ethtool_coalesce *ecmd) 190 { 191 struct enic *enic = netdev_priv(netdev); 192 struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; 193 194 ecmd->tx_coalesce_usecs = enic->tx_coalesce_usecs; 195 ecmd->rx_coalesce_usecs = enic->rx_coalesce_usecs; 196 if (rxcoal->use_adaptive_rx_coalesce) 197 ecmd->use_adaptive_rx_coalesce = 1; 198 ecmd->rx_coalesce_usecs_low = rxcoal->small_pkt_range_start; 199 ecmd->rx_coalesce_usecs_high = rxcoal->range_end; 200 201 return 0; 202 } 203 204 static int enic_set_coalesce(struct net_device *netdev, 205 struct ethtool_coalesce *ecmd) 206 { 207 struct enic *enic = netdev_priv(netdev); 208 u32 tx_coalesce_usecs; 209 u32 rx_coalesce_usecs; 210 u32 rx_coalesce_usecs_low; 211 u32 rx_coalesce_usecs_high; 212 u32 coalesce_usecs_max; 213 unsigned int i, intr; 214 struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; 215 216 coalesce_usecs_max = vnic_dev_get_intr_coal_timer_max(enic->vdev); 217 tx_coalesce_usecs = min_t(u32, ecmd->tx_coalesce_usecs, 218 coalesce_usecs_max); 219 rx_coalesce_usecs = min_t(u32, ecmd->rx_coalesce_usecs, 220 coalesce_usecs_max); 221 222 rx_coalesce_usecs_low = min_t(u32, ecmd->rx_coalesce_usecs_low, 223 coalesce_usecs_max); 224 rx_coalesce_usecs_high = min_t(u32, ecmd->rx_coalesce_usecs_high, 225 coalesce_usecs_max); 226 227 switch (vnic_dev_get_intr_mode(enic->vdev)) { 228 case VNIC_DEV_INTR_MODE_INTX: 229 if (tx_coalesce_usecs != rx_coalesce_usecs) 230 return -EINVAL; 231 if (ecmd->use_adaptive_rx_coalesce || 232 ecmd->rx_coalesce_usecs_low || 233 ecmd->rx_coalesce_usecs_high) 234 return -EOPNOTSUPP; 235 236 intr = enic_legacy_io_intr(); 237 vnic_intr_coalescing_timer_set(&enic->intr[intr], 238 tx_coalesce_usecs); 239 break; 240 case VNIC_DEV_INTR_MODE_MSI: 241 if (tx_coalesce_usecs != rx_coalesce_usecs) 242 return -EINVAL; 243 if (ecmd->use_adaptive_rx_coalesce || 244 ecmd->rx_coalesce_usecs_low || 245 ecmd->rx_coalesce_usecs_high) 246 return -EOPNOTSUPP; 247 248 vnic_intr_coalescing_timer_set(&enic->intr[0], 249 tx_coalesce_usecs); 250 break; 251 case VNIC_DEV_INTR_MODE_MSIX: 252 for (i = 0; i < enic->wq_count; i++) { 253 intr = enic_msix_wq_intr(enic, i); 254 vnic_intr_coalescing_timer_set(&enic->intr[intr], 255 tx_coalesce_usecs); 256 } 257 258 if (rxcoal->use_adaptive_rx_coalesce) { 259 if (!ecmd->use_adaptive_rx_coalesce) { 260 rxcoal->use_adaptive_rx_coalesce = 0; 261 enic_intr_coal_set_rx(enic, rx_coalesce_usecs); 262 } 263 } else { 264 if (ecmd->use_adaptive_rx_coalesce) 265 rxcoal->use_adaptive_rx_coalesce = 1; 266 else 267 enic_intr_coal_set_rx(enic, rx_coalesce_usecs); 268 } 269 270 if (ecmd->rx_coalesce_usecs_high) { 271 if (rx_coalesce_usecs_high < 272 (rx_coalesce_usecs_low + ENIC_AIC_LARGE_PKT_DIFF)) 273 return -EINVAL; 274 rxcoal->range_end = rx_coalesce_usecs_high; 275 rxcoal->small_pkt_range_start = rx_coalesce_usecs_low; 276 rxcoal->large_pkt_range_start = rx_coalesce_usecs_low + 277 ENIC_AIC_LARGE_PKT_DIFF; 278 } 279 break; 280 default: 281 break; 282 } 283 284 enic->tx_coalesce_usecs = tx_coalesce_usecs; 285 enic->rx_coalesce_usecs = rx_coalesce_usecs; 286 287 return 0; 288 } 289 290 static const struct ethtool_ops enic_ethtool_ops = { 291 .get_settings = enic_get_settings, 292 .get_drvinfo = enic_get_drvinfo, 293 .get_msglevel = enic_get_msglevel, 294 .set_msglevel = enic_set_msglevel, 295 .get_link = ethtool_op_get_link, 296 .get_strings = enic_get_strings, 297 .get_sset_count = enic_get_sset_count, 298 .get_ethtool_stats = enic_get_ethtool_stats, 299 .get_coalesce = enic_get_coalesce, 300 .set_coalesce = enic_set_coalesce, 301 }; 302 303 void enic_set_ethtool_ops(struct net_device *netdev) 304 { 305 netdev->ethtool_ops = &enic_ethtool_ops; 306 } 307