summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/intel/e1000e/ethtool.c
diff options
context:
space:
mode:
authorLen Brown <len.brown@intel.com>2012-04-07 05:48:59 +0400
committerLen Brown <len.brown@intel.com>2012-04-07 05:48:59 +0400
commiteeaab2d8af2cf1d36d7086f22e9de42d6dd2995c (patch)
tree369b9c91a6d808944f07d2290fec6f9fe2731904 /drivers/net/ethernet/intel/e1000e/ethtool.c
parentee01e663373343c63e0e3d364d09f6155378dbcc (diff)
parentaaef292acf3a78d9c0bb6fb72226077d286b45d7 (diff)
downloadlinux-eeaab2d8af2cf1d36d7086f22e9de42d6dd2995c.tar.xz
Merge branches 'idle-fix' and 'misc' into release
Diffstat (limited to 'drivers/net/ethernet/intel/e1000e/ethtool.c')
-rw-r--r--drivers/net/ethernet/intel/e1000e/ethtool.c207
1 files changed, 138 insertions, 69 deletions
diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c
index fb2c28e799a2..db35dd5d96de 100644
--- a/drivers/net/ethernet/intel/e1000e/ethtool.c
+++ b/drivers/net/ethernet/intel/e1000e/ethtool.c
@@ -1,7 +1,7 @@
/*******************************************************************************
Intel PRO/1000 Linux driver
- Copyright(c) 1999 - 2011 Intel Corporation.
+ Copyright(c) 1999 - 2012 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
@@ -34,6 +34,7 @@
#include <linux/pci.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/vmalloc.h>
#include "e1000.h"
@@ -257,7 +258,7 @@ static int e1000_set_settings(struct net_device *netdev,
* When SoL/IDER sessions are active, autoneg/speed/duplex
* cannot be changed
*/
- if (e1000_check_reset_block(hw)) {
+ if (hw->phy.ops.check_reset_block(hw)) {
e_err("Cannot change link characteristics when SoL/IDER is "
"active.\n");
return -EINVAL;
@@ -536,7 +537,7 @@ static int e1000_set_eeprom(struct net_device *netdev,
ret_val = e1000_read_nvm(hw, first_word, 1, &eeprom_buff[0]);
ptr++;
}
- if (((eeprom->offset + eeprom->len) & 1) && (ret_val == 0))
+ if (((eeprom->offset + eeprom->len) & 1) && (!ret_val))
/* need read/modify/write of last changed EEPROM word */
/* only the first byte of the word is being modified */
ret_val = e1000_read_nvm(hw, last_word, 1,
@@ -552,7 +553,7 @@ static int e1000_set_eeprom(struct net_device *netdev,
memcpy(ptr, bytes, eeprom->len);
for (i = 0; i < last_word - first_word + 1; i++)
- eeprom_buff[i] = cpu_to_le16(eeprom_buff[i]);
+ cpu_to_le16s(&eeprom_buff[i]);
ret_val = e1000_write_nvm(hw, first_word,
last_word - first_word + 1, eeprom_buff);
@@ -605,94 +606,112 @@ static void e1000_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring)
{
struct e1000_adapter *adapter = netdev_priv(netdev);
- struct e1000_ring *tx_ring = adapter->tx_ring;
- struct e1000_ring *rx_ring = adapter->rx_ring;
ring->rx_max_pending = E1000_MAX_RXD;
ring->tx_max_pending = E1000_MAX_TXD;
- ring->rx_pending = rx_ring->count;
- ring->tx_pending = tx_ring->count;
+ ring->rx_pending = adapter->rx_ring_count;
+ ring->tx_pending = adapter->tx_ring_count;
}
static int e1000_set_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring)
{
struct e1000_adapter *adapter = netdev_priv(netdev);
- struct e1000_ring *tx_ring, *tx_old;
- struct e1000_ring *rx_ring, *rx_old;
- int err;
+ struct e1000_ring *temp_tx = NULL, *temp_rx = NULL;
+ int err = 0, size = sizeof(struct e1000_ring);
+ bool set_tx = false, set_rx = false;
+ u16 new_rx_count, new_tx_count;
if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
return -EINVAL;
- while (test_and_set_bit(__E1000_RESETTING, &adapter->state))
- usleep_range(1000, 2000);
+ new_rx_count = clamp_t(u32, ring->rx_pending, E1000_MIN_RXD,
+ E1000_MAX_RXD);
+ new_rx_count = ALIGN(new_rx_count, REQ_RX_DESCRIPTOR_MULTIPLE);
- if (netif_running(adapter->netdev))
- e1000e_down(adapter);
+ new_tx_count = clamp_t(u32, ring->tx_pending, E1000_MIN_TXD,
+ E1000_MAX_TXD);
+ new_tx_count = ALIGN(new_tx_count, REQ_TX_DESCRIPTOR_MULTIPLE);
- tx_old = adapter->tx_ring;
- rx_old = adapter->rx_ring;
+ if ((new_tx_count == adapter->tx_ring_count) &&
+ (new_rx_count == adapter->rx_ring_count))
+ /* nothing to do */
+ return 0;
- err = -ENOMEM;
- tx_ring = kmemdup(tx_old, sizeof(struct e1000_ring), GFP_KERNEL);
- if (!tx_ring)
- goto err_alloc_tx;
+ while (test_and_set_bit(__E1000_RESETTING, &adapter->state))
+ usleep_range(1000, 2000);
- rx_ring = kmemdup(rx_old, sizeof(struct e1000_ring), GFP_KERNEL);
- if (!rx_ring)
- goto err_alloc_rx;
+ if (!netif_running(adapter->netdev)) {
+ /* Set counts now and allocate resources during open() */
+ adapter->tx_ring->count = new_tx_count;
+ adapter->rx_ring->count = new_rx_count;
+ adapter->tx_ring_count = new_tx_count;
+ adapter->rx_ring_count = new_rx_count;
+ goto clear_reset;
+ }
- adapter->tx_ring = tx_ring;
- adapter->rx_ring = rx_ring;
+ set_tx = (new_tx_count != adapter->tx_ring_count);
+ set_rx = (new_rx_count != adapter->rx_ring_count);
- rx_ring->count = max(ring->rx_pending, (u32)E1000_MIN_RXD);
- rx_ring->count = min(rx_ring->count, (u32)(E1000_MAX_RXD));
- rx_ring->count = ALIGN(rx_ring->count, REQ_RX_DESCRIPTOR_MULTIPLE);
+ /* Allocate temporary storage for ring updates */
+ if (set_tx) {
+ temp_tx = vmalloc(size);
+ if (!temp_tx) {
+ err = -ENOMEM;
+ goto free_temp;
+ }
+ }
+ if (set_rx) {
+ temp_rx = vmalloc(size);
+ if (!temp_rx) {
+ err = -ENOMEM;
+ goto free_temp;
+ }
+ }
- tx_ring->count = max(ring->tx_pending, (u32)E1000_MIN_TXD);
- tx_ring->count = min(tx_ring->count, (u32)(E1000_MAX_TXD));
- tx_ring->count = ALIGN(tx_ring->count, REQ_TX_DESCRIPTOR_MULTIPLE);
+ e1000e_down(adapter);
- if (netif_running(adapter->netdev)) {
- /* Try to get new resources before deleting old */
- err = e1000e_setup_rx_resources(adapter);
+ /*
+ * We can't just free everything and then setup again, because the
+ * ISRs in MSI-X mode get passed pointers to the Tx and Rx ring
+ * structs. First, attempt to allocate new resources...
+ */
+ if (set_tx) {
+ memcpy(temp_tx, adapter->tx_ring, size);
+ temp_tx->count = new_tx_count;
+ err = e1000e_setup_tx_resources(temp_tx);
if (err)
- goto err_setup_rx;
- err = e1000e_setup_tx_resources(adapter);
+ goto err_setup;
+ }
+ if (set_rx) {
+ memcpy(temp_rx, adapter->rx_ring, size);
+ temp_rx->count = new_rx_count;
+ err = e1000e_setup_rx_resources(temp_rx);
if (err)
- goto err_setup_tx;
+ goto err_setup_rx;
+ }
- /*
- * restore the old in order to free it,
- * then add in the new
- */
- adapter->rx_ring = rx_old;
- adapter->tx_ring = tx_old;
- e1000e_free_rx_resources(adapter);
- e1000e_free_tx_resources(adapter);
- kfree(tx_old);
- kfree(rx_old);
- adapter->rx_ring = rx_ring;
- adapter->tx_ring = tx_ring;
- err = e1000e_up(adapter);
- if (err)
- goto err_setup;
+ /* ...then free the old resources and copy back any new ring data */
+ if (set_tx) {
+ e1000e_free_tx_resources(adapter->tx_ring);
+ memcpy(adapter->tx_ring, temp_tx, size);
+ adapter->tx_ring_count = new_tx_count;
+ }
+ if (set_rx) {
+ e1000e_free_rx_resources(adapter->rx_ring);
+ memcpy(adapter->rx_ring, temp_rx, size);
+ adapter->rx_ring_count = new_rx_count;
}
- clear_bit(__E1000_RESETTING, &adapter->state);
- return 0;
-err_setup_tx:
- e1000e_free_rx_resources(adapter);
err_setup_rx:
- adapter->rx_ring = rx_old;
- adapter->tx_ring = tx_old;
- kfree(rx_ring);
-err_alloc_rx:
- kfree(tx_ring);
-err_alloc_tx:
- e1000e_up(adapter);
+ if (err && set_tx)
+ e1000e_free_tx_resources(temp_tx);
err_setup:
+ e1000e_up(adapter);
+free_temp:
+ vfree(temp_tx);
+ vfree(temp_rx);
+clear_reset:
clear_bit(__E1000_RESETTING, &adapter->state);
return err;
}
@@ -1069,7 +1088,7 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
tx_ring->buffer_info = kcalloc(tx_ring->count,
sizeof(struct e1000_buffer),
GFP_KERNEL);
- if (!(tx_ring->buffer_info)) {
+ if (!tx_ring->buffer_info) {
ret_val = 1;
goto err_nomem;
}
@@ -1131,7 +1150,7 @@ static int e1000_setup_desc_rings(struct e1000_adapter *adapter)
rx_ring->buffer_info = kcalloc(rx_ring->count,
sizeof(struct e1000_buffer),
GFP_KERNEL);
- if (!(rx_ring->buffer_info)) {
+ if (!rx_ring->buffer_info) {
ret_val = 5;
goto err_nomem;
}
@@ -1579,11 +1598,13 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter)
static int e1000_loopback_test(struct e1000_adapter *adapter, u64 *data)
{
+ struct e1000_hw *hw = &adapter->hw;
+
/*
* PHY loopback cannot be performed if SoL/IDER
* sessions are active
*/
- if (e1000_check_reset_block(&adapter->hw)) {
+ if (hw->phy.ops.check_reset_block(hw)) {
e_err("Cannot do PHY loopback test when SoL/IDER is active.\n");
*data = 0;
goto out;
@@ -1837,11 +1858,11 @@ static int e1000_set_phys_id(struct net_device *netdev,
break;
case ETHTOOL_ID_ON:
- adapter->hw.mac.ops.led_on(&adapter->hw);
+ hw->mac.ops.led_on(hw);
break;
case ETHTOOL_ID_OFF:
- adapter->hw.mac.ops.led_off(&adapter->hw);
+ hw->mac.ops.led_off(hw);
break;
}
return 0;
@@ -1955,6 +1976,53 @@ static void e1000_get_strings(struct net_device *netdev, u32 stringset,
}
}
+static int e1000_get_rxnfc(struct net_device *netdev,
+ struct ethtool_rxnfc *info, u32 *rule_locs)
+{
+ info->data = 0;
+
+ switch (info->cmd) {
+ case ETHTOOL_GRXFH: {
+ struct e1000_adapter *adapter = netdev_priv(netdev);
+ struct e1000_hw *hw = &adapter->hw;
+ u32 mrqc = er32(MRQC);
+
+ if (!(mrqc & E1000_MRQC_RSS_FIELD_MASK))
+ return 0;
+
+ switch (info->flow_type) {
+ case TCP_V4_FLOW:
+ if (mrqc & E1000_MRQC_RSS_FIELD_IPV4_TCP)
+ 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:
+ if (mrqc & E1000_MRQC_RSS_FIELD_IPV4)
+ info->data |= RXH_IP_SRC | RXH_IP_DST;
+ break;
+ case TCP_V6_FLOW:
+ if (mrqc & E1000_MRQC_RSS_FIELD_IPV6_TCP)
+ 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:
+ if (mrqc & E1000_MRQC_RSS_FIELD_IPV6)
+ info->data |= RXH_IP_SRC | RXH_IP_DST;
+ break;
+ default:
+ break;
+ }
+ return 0;
+ }
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
static const struct ethtool_ops e1000_ethtool_ops = {
.get_settings = e1000_get_settings,
.set_settings = e1000_set_settings,
@@ -1981,6 +2049,7 @@ static const struct ethtool_ops e1000_ethtool_ops = {
.get_sset_count = e1000e_get_sset_count,
.get_coalesce = e1000_get_coalesce,
.set_coalesce = e1000_set_coalesce,
+ .get_rxnfc = e1000_get_rxnfc,
};
void e1000e_set_ethtool_ops(struct net_device *netdev)