Staging: Merge staging-next into Linus's tree
[sfrench/cifs-2.6.git] / drivers / net / sfc / ethtool.c
index 22026bfbc4c1e8adbef1bacf30079edb0bf2f1c3..fd19d6ab97a256259db66636f2856bc3bf8ebe26 100644 (file)
@@ -218,8 +218,8 @@ int efx_ethtool_set_settings(struct net_device *net_dev,
 
        /* GMAC does not support 1000Mbps HD */
        if (ecmd->speed == SPEED_1000 && ecmd->duplex != DUPLEX_FULL) {
-               EFX_LOG(efx, "rejecting unsupported 1000Mbps HD"
-                       " setting\n");
+               netif_dbg(efx, drv, efx->net_dev,
+                         "rejecting unsupported 1000Mbps HD setting\n");
                return -EINVAL;
        }
 
@@ -234,7 +234,7 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev,
 {
        struct efx_nic *efx = netdev_priv(net_dev);
 
-       strlcpy(info->driver, EFX_DRIVER_NAME, sizeof(info->driver));
+       strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
        strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version));
        if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0)
                siena_print_fwver(efx, info->fw_version,
@@ -242,6 +242,32 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev,
        strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info));
 }
 
+static int efx_ethtool_get_regs_len(struct net_device *net_dev)
+{
+       return efx_nic_get_regs_len(netdev_priv(net_dev));
+}
+
+static void efx_ethtool_get_regs(struct net_device *net_dev,
+                                struct ethtool_regs *regs, void *buf)
+{
+       struct efx_nic *efx = netdev_priv(net_dev);
+
+       regs->version = efx->type->revision;
+       efx_nic_get_regs(efx, buf);
+}
+
+static u32 efx_ethtool_get_msglevel(struct net_device *net_dev)
+{
+       struct efx_nic *efx = netdev_priv(net_dev);
+       return efx->msg_enable;
+}
+
+static void efx_ethtool_set_msglevel(struct net_device *net_dev, u32 msg_enable)
+{
+       struct efx_nic *efx = netdev_priv(net_dev);
+       efx->msg_enable = msg_enable;
+}
+
 /**
  * efx_fill_test - fill in an individual self-test entry
  * @test_index:                Index of the test
@@ -443,12 +469,13 @@ static void efx_ethtool_get_stats(struct net_device *net_dev,
        struct efx_mac_stats *mac_stats = &efx->mac_stats;
        struct efx_ethtool_stat *stat;
        struct efx_channel *channel;
+       struct rtnl_link_stats64 temp;
        int i;
 
        EFX_BUG_ON_PARANOID(stats->n_stats != EFX_ETHTOOL_NUM_STATS);
 
        /* Update MAC and NIC statistics */
-       dev_get_stats(net_dev);
+       dev_get_stats(net_dev, &temp);
 
        /* Fill detailed statistics buffer */
        for (i = 0; i < EFX_ETHTOOL_NUM_STATS; i++) {
@@ -520,6 +547,14 @@ static u32 efx_ethtool_get_rx_csum(struct net_device *net_dev)
        return efx->rx_checksum_enabled;
 }
 
+static int efx_ethtool_set_flags(struct net_device *net_dev, u32 data)
+{
+       struct efx_nic *efx = netdev_priv(net_dev);
+       u32 supported = efx->type->offload_features & ETH_FLAG_RXHASH;
+
+       return ethtool_op_set_flags(net_dev, data, supported);
+}
+
 static void efx_ethtool_self_test(struct net_device *net_dev,
                                  struct ethtool_test *test, u64 *data)
 {
@@ -539,7 +574,8 @@ static void efx_ethtool_self_test(struct net_device *net_dev,
        if (!already_up) {
                rc = dev_open(efx->net_dev);
                if (rc) {
-                       EFX_ERR(efx, "failed opening device.\n");
+                       netif_err(efx, drv, efx->net_dev,
+                                 "failed opening device.\n");
                        goto fail2;
                }
        }
@@ -551,9 +587,9 @@ static void efx_ethtool_self_test(struct net_device *net_dev,
        if (!already_up)
                dev_close(efx->net_dev);
 
-       EFX_LOG(efx, "%s %sline self-tests\n",
-               rc == 0 ? "passed" : "failed",
-               (test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on");
+       netif_dbg(efx, drv, efx->net_dev, "%s %sline self-tests\n",
+                 rc == 0 ? "passed" : "failed",
+                 (test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on");
 
  fail2:
  fail1:
@@ -679,8 +715,8 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev,
                return -EOPNOTSUPP;
 
        if (coalesce->rx_coalesce_usecs || coalesce->tx_coalesce_usecs) {
-               EFX_ERR(efx, "invalid coalescing setting. "
-                       "Only rx/tx_coalesce_usecs_irq are supported\n");
+               netif_err(efx, drv, efx->net_dev, "invalid coalescing setting. "
+                         "Only rx/tx_coalesce_usecs_irq are supported\n");
                return -EOPNOTSUPP;
        }
 
@@ -692,8 +728,8 @@ static int efx_ethtool_set_coalesce(struct net_device *net_dev,
        efx_for_each_tx_queue(tx_queue, efx) {
                if ((tx_queue->channel->channel < efx->n_rx_channels) &&
                    tx_usecs) {
-                       EFX_ERR(efx, "Channel is shared. "
-                               "Only RX coalescing may be set\n");
+                       netif_err(efx, drv, efx->net_dev, "Channel is shared. "
+                                 "Only RX coalescing may be set\n");
                        return -EOPNOTSUPP;
                }
        }
@@ -721,13 +757,15 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
                     (pause->autoneg ? EFX_FC_AUTO : 0));
 
        if ((wanted_fc & EFX_FC_TX) && !(wanted_fc & EFX_FC_RX)) {
-               EFX_LOG(efx, "Flow control unsupported: tx ON rx OFF\n");
+               netif_dbg(efx, drv, efx->net_dev,
+                         "Flow control unsupported: tx ON rx OFF\n");
                rc = -EINVAL;
                goto out;
        }
 
        if ((wanted_fc & EFX_FC_AUTO) && !efx->link_advertising) {
-               EFX_LOG(efx, "Autonegotiation is disabled\n");
+               netif_dbg(efx, drv, efx->net_dev,
+                         "Autonegotiation is disabled\n");
                rc = -EINVAL;
                goto out;
        }
@@ -758,8 +796,9 @@ static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
            (efx->wanted_fc ^ old_fc) & EFX_FC_AUTO) {
                rc = efx->phy_op->reconfigure(efx);
                if (rc) {
-                       EFX_ERR(efx, "Unable to advertise requested flow "
-                               "control setting\n");
+                       netif_err(efx, drv, efx->net_dev,
+                                 "Unable to advertise requested flow "
+                                 "control setting\n");
                        goto out;
                }
        }
@@ -830,10 +869,101 @@ extern int efx_ethtool_reset(struct net_device *net_dev, u32 *flags)
        return efx_reset(efx, method);
 }
 
+static int
+efx_ethtool_get_rxnfc(struct net_device *net_dev,
+                     struct ethtool_rxnfc *info, void *rules __always_unused)
+{
+       struct efx_nic *efx = netdev_priv(net_dev);
+
+       switch (info->cmd) {
+       case ETHTOOL_GRXRINGS:
+               info->data = efx->n_rx_channels;
+               return 0;
+
+       case ETHTOOL_GRXFH: {
+               unsigned min_revision = 0;
+
+               info->data = 0;
+               switch (info->flow_type) {
+               case TCP_V4_FLOW:
+                       info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+                       /* fall through */
+               case UDP_V4_FLOW:
+               case SCTP_V4_FLOW:
+               case AH_ESP_V4_FLOW:
+               case IPV4_FLOW:
+                       info->data |= RXH_IP_SRC | RXH_IP_DST;
+                       min_revision = EFX_REV_FALCON_B0;
+                       break;
+               case TCP_V6_FLOW:
+                       info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+                       /* fall through */
+               case UDP_V6_FLOW:
+               case SCTP_V6_FLOW:
+               case AH_ESP_V6_FLOW:
+               case IPV6_FLOW:
+                       info->data |= RXH_IP_SRC | RXH_IP_DST;
+                       min_revision = EFX_REV_SIENA_A0;
+                       break;
+               default:
+                       break;
+               }
+               if (efx_nic_rev(efx) < min_revision)
+                       info->data = 0;
+               return 0;
+       }
+
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int efx_ethtool_get_rxfh_indir(struct net_device *net_dev,
+                                     struct ethtool_rxfh_indir *indir)
+{
+       struct efx_nic *efx = netdev_priv(net_dev);
+       size_t copy_size =
+               min_t(size_t, indir->size, ARRAY_SIZE(efx->rx_indir_table));
+
+       if (efx_nic_rev(efx) < EFX_REV_FALCON_B0)
+               return -EOPNOTSUPP;
+
+       indir->size = ARRAY_SIZE(efx->rx_indir_table);
+       memcpy(indir->ring_index, efx->rx_indir_table,
+              copy_size * sizeof(indir->ring_index[0]));
+       return 0;
+}
+
+static int efx_ethtool_set_rxfh_indir(struct net_device *net_dev,
+                                     const struct ethtool_rxfh_indir *indir)
+{
+       struct efx_nic *efx = netdev_priv(net_dev);
+       size_t i;
+
+       if (efx_nic_rev(efx) < EFX_REV_FALCON_B0)
+               return -EOPNOTSUPP;
+
+       /* Validate size and indices */
+       if (indir->size != ARRAY_SIZE(efx->rx_indir_table))
+               return -EINVAL;
+       for (i = 0; i < ARRAY_SIZE(efx->rx_indir_table); i++)
+               if (indir->ring_index[i] >= efx->n_rx_channels)
+                       return -EINVAL;
+
+       memcpy(efx->rx_indir_table, indir->ring_index,
+              sizeof(efx->rx_indir_table));
+       efx_nic_push_rx_indir_table(efx);
+       return 0;
+}
+
 const struct ethtool_ops efx_ethtool_ops = {
        .get_settings           = efx_ethtool_get_settings,
        .set_settings           = efx_ethtool_set_settings,
        .get_drvinfo            = efx_ethtool_get_drvinfo,
+       .get_regs_len           = efx_ethtool_get_regs_len,
+       .get_regs               = efx_ethtool_get_regs,
+       .get_msglevel           = efx_ethtool_get_msglevel,
+       .set_msglevel           = efx_ethtool_set_msglevel,
        .nway_reset             = efx_ethtool_nway_reset,
        .get_link               = efx_ethtool_get_link,
        .get_eeprom_len         = efx_ethtool_get_eeprom_len,
@@ -854,7 +984,7 @@ const struct ethtool_ops efx_ethtool_ops = {
        /* Need to enable/disable TSO-IPv6 too */
        .set_tso                = efx_ethtool_set_tso,
        .get_flags              = ethtool_op_get_flags,
-       .set_flags              = ethtool_op_set_flags,
+       .set_flags              = efx_ethtool_set_flags,
        .get_sset_count         = efx_ethtool_get_sset_count,
        .self_test              = efx_ethtool_self_test,
        .get_strings            = efx_ethtool_get_strings,
@@ -863,4 +993,7 @@ const struct ethtool_ops efx_ethtool_ops = {
        .get_wol                = efx_ethtool_get_wol,
        .set_wol                = efx_ethtool_set_wol,
        .reset                  = efx_ethtool_reset,
+       .get_rxnfc              = efx_ethtool_get_rxnfc,
+       .get_rxfh_indir         = efx_ethtool_get_rxfh_indir,
+       .set_rxfh_indir         = efx_ethtool_set_rxfh_indir,
 };