aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/emulex/benet/be_ethtool.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/emulex/benet/be_ethtool.c')
-rw-r--r--drivers/net/ethernet/emulex/benet/be_ethtool.c539
1 files changed, 381 insertions, 158 deletions
diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c
index 00454a10f88..e2da4d20dd3 100644
--- a/drivers/net/ethernet/emulex/benet/be_ethtool.c
+++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005 - 2011 Emulex
+ * Copyright (C) 2005 - 2014 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
@@ -54,7 +54,7 @@ static const struct be_ethtool_stat et_stats[] = {
/* Received packets dropped when they don't pass the unicast or
* multicast address filtering.
*/
- {DRVSTAT_INFO(rx_address_mismatch_drops)},
+ {DRVSTAT_INFO(rx_address_filtered)},
/* Received packets dropped when IP packet length field is less than
* the IP header length field.
*/
@@ -85,6 +85,7 @@ static const struct be_ethtool_stat et_stats[] = {
{DRVSTAT_INFO(tx_pauseframes)},
{DRVSTAT_INFO(tx_controlframes)},
{DRVSTAT_INFO(rx_priority_pause_frames)},
+ {DRVSTAT_INFO(tx_priority_pauseframes)},
/* Received packets dropped when an internal fifo going into
* main packet buffer tank (PMEM) overflows.
*/
@@ -115,7 +116,12 @@ static const struct be_ethtool_stat et_stats[] = {
{DRVSTAT_INFO(rx_drops_mtu)},
/* Number of packets dropped due to random early drop function */
{DRVSTAT_INFO(eth_red_drops)},
- {DRVSTAT_INFO(be_on_die_temperature)}
+ {DRVSTAT_INFO(be_on_die_temperature)},
+ {DRVSTAT_INFO(rx_roce_bytes_lsd)},
+ {DRVSTAT_INFO(rx_roce_bytes_msd)},
+ {DRVSTAT_INFO(rx_roce_frames)},
+ {DRVSTAT_INFO(roce_drops_payload_len)},
+ {DRVSTAT_INFO(roce_drops_crc)}
};
#define ETHTOOL_STATS_NUM ARRAY_SIZE(et_stats)
@@ -126,6 +132,7 @@ static const struct be_ethtool_stat et_rx_stats[] = {
{DRVSTAT_RX_INFO(rx_bytes)},/* If moving this member see above note */
{DRVSTAT_RX_INFO(rx_pkts)}, /* If moving this member see above note */
{DRVSTAT_RX_INFO(rx_compl)},
+ {DRVSTAT_RX_INFO(rx_compl_err)},
{DRVSTAT_RX_INFO(rx_mcast_pkts)},
/* Number of page allocation failures while posting receive buffers
* to HW.
@@ -154,7 +161,9 @@ static const struct be_ethtool_stat et_tx_stats[] = {
/* Number of times the TX queue was stopped due to lack
* of spaces in the TXQ.
*/
- {DRVSTAT_TX_INFO(tx_stops)}
+ {DRVSTAT_TX_INFO(tx_stops)},
+ /* Pkts dropped in the driver's transmit path */
+ {DRVSTAT_TX_INFO(tx_drv_drops)}
};
#define ETHTOOL_TXSTATS_NUM (ARRAY_SIZE(et_tx_stats))
@@ -173,22 +182,18 @@ static const char et_self_tests[][ETH_GSTRING_LEN] = {
#define BE_NO_LOOPBACK 0xff
static void be_get_drvinfo(struct net_device *netdev,
- struct ethtool_drvinfo *drvinfo)
+ struct ethtool_drvinfo *drvinfo)
{
struct be_adapter *adapter = netdev_priv(netdev);
- char fw_on_flash[FW_VER_LEN];
-
- memset(fw_on_flash, 0 , sizeof(fw_on_flash));
- be_cmd_get_fw_ver(adapter, adapter->fw_ver, fw_on_flash);
strlcpy(drvinfo->driver, DRV_NAME, sizeof(drvinfo->driver));
strlcpy(drvinfo->version, DRV_VER, sizeof(drvinfo->version));
- strncpy(drvinfo->fw_version, adapter->fw_ver, FW_VER_LEN);
- if (memcmp(adapter->fw_ver, fw_on_flash, FW_VER_LEN) != 0) {
- strcat(drvinfo->fw_version, " [");
- strcat(drvinfo->fw_version, fw_on_flash);
- strcat(drvinfo->fw_version, "]");
- }
+ if (!memcmp(adapter->fw_ver, adapter->fw_on_flash, FW_VER_LEN))
+ strlcpy(drvinfo->fw_version, adapter->fw_ver,
+ sizeof(drvinfo->fw_version));
+ else
+ snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version),
+ "%s [%s]", adapter->fw_ver, adapter->fw_on_flash);
strlcpy(drvinfo->bus_info, pci_name(adapter->pdev),
sizeof(drvinfo->bus_info));
@@ -197,8 +202,7 @@ static void be_get_drvinfo(struct net_device *netdev,
drvinfo->eedump_len = 0;
}
-static u32
-lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name)
+static u32 lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name)
{
u32 data_read = 0, eof;
u8 addn_status;
@@ -208,14 +212,14 @@ lancer_cmd_get_file_len(struct be_adapter *adapter, u8 *file_name)
memset(&data_len_cmd, 0, sizeof(data_len_cmd));
/* data_offset and data_size should be 0 to get reg len */
status = lancer_cmd_read_object(adapter, &data_len_cmd, 0, 0,
- file_name, &data_read, &eof, &addn_status);
+ file_name, &data_read, &eof,
+ &addn_status);
return data_read;
}
-static int
-lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
- u32 buf_len, void *buf)
+static int lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
+ u32 buf_len, void *buf)
{
struct be_dma_mem read_cmd;
u32 read_len = 0, total_read_len = 0, chunk_size;
@@ -225,11 +229,11 @@ lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
read_cmd.size = LANCER_READ_FILE_CHUNK;
read_cmd.va = pci_alloc_consistent(adapter->pdev, read_cmd.size,
- &read_cmd.dma);
+ &read_cmd.dma);
if (!read_cmd.va) {
dev_err(&adapter->pdev->dev,
- "Memory allocation failure while reading dump\n");
+ "Memory allocation failure while reading dump\n");
return -ENOMEM;
}
@@ -238,8 +242,8 @@ lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
LANCER_READ_FILE_CHUNK);
chunk_size = ALIGN(chunk_size, 4);
status = lancer_cmd_read_object(adapter, &read_cmd, chunk_size,
- total_read_len, file_name, &read_len,
- &eof, &addn_status);
+ total_read_len, file_name,
+ &read_len, &eof, &addn_status);
if (!status) {
memcpy(buf + total_read_len, read_cmd.va, read_len);
total_read_len += read_len;
@@ -250,13 +254,12 @@ lancer_cmd_read_file(struct be_adapter *adapter, u8 *file_name,
}
}
pci_free_consistent(adapter->pdev, read_cmd.size, read_cmd.va,
- read_cmd.dma);
+ read_cmd.dma);
return status;
}
-static int
-be_get_reg_len(struct net_device *netdev)
+static int be_get_reg_len(struct net_device *netdev)
{
struct be_adapter *adapter = netdev_priv(netdev);
u32 log_size = 0;
@@ -267,7 +270,7 @@ be_get_reg_len(struct net_device *netdev)
if (be_physfn(adapter)) {
if (lancer_chip(adapter))
log_size = lancer_cmd_get_file_len(adapter,
- LANCER_FW_DUMP_FILE);
+ LANCER_FW_DUMP_FILE);
else
be_cmd_get_reg_len(adapter, &log_size);
}
@@ -283,7 +286,7 @@ be_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *buf)
memset(buf, 0, regs->len);
if (lancer_chip(adapter))
lancer_cmd_read_file(adapter, LANCER_FW_DUMP_FILE,
- regs->len, buf);
+ regs->len, buf);
else
be_cmd_get_regs(adapter, regs->len, buf);
}
@@ -293,19 +296,19 @@ static int be_get_coalesce(struct net_device *netdev,
struct ethtool_coalesce *et)
{
struct be_adapter *adapter = netdev_priv(netdev);
- struct be_eq_obj *eqo = &adapter->eq_obj[0];
+ struct be_aic_obj *aic = &adapter->aic_obj[0];
- et->rx_coalesce_usecs = eqo->cur_eqd;
- et->rx_coalesce_usecs_high = eqo->max_eqd;
- et->rx_coalesce_usecs_low = eqo->min_eqd;
+ et->rx_coalesce_usecs = aic->prev_eqd;
+ et->rx_coalesce_usecs_high = aic->max_eqd;
+ et->rx_coalesce_usecs_low = aic->min_eqd;
- et->tx_coalesce_usecs = eqo->cur_eqd;
- et->tx_coalesce_usecs_high = eqo->max_eqd;
- et->tx_coalesce_usecs_low = eqo->min_eqd;
+ et->tx_coalesce_usecs = aic->prev_eqd;
+ et->tx_coalesce_usecs_high = aic->max_eqd;
+ et->tx_coalesce_usecs_low = aic->min_eqd;
- et->use_adaptive_rx_coalesce = eqo->enable_aic;
- et->use_adaptive_tx_coalesce = eqo->enable_aic;
+ et->use_adaptive_rx_coalesce = aic->enable;
+ et->use_adaptive_tx_coalesce = aic->enable;
return 0;
}
@@ -317,22 +320,24 @@ static int be_set_coalesce(struct net_device *netdev,
struct ethtool_coalesce *et)
{
struct be_adapter *adapter = netdev_priv(netdev);
+ struct be_aic_obj *aic = &adapter->aic_obj[0];
struct be_eq_obj *eqo;
int i;
for_all_evt_queues(adapter, eqo, i) {
- eqo->enable_aic = et->use_adaptive_rx_coalesce;
- eqo->max_eqd = min(et->rx_coalesce_usecs_high, BE_MAX_EQD);
- eqo->min_eqd = min(et->rx_coalesce_usecs_low, eqo->max_eqd);
- eqo->eqd = et->rx_coalesce_usecs;
+ aic->enable = et->use_adaptive_rx_coalesce;
+ aic->max_eqd = min(et->rx_coalesce_usecs_high, BE_MAX_EQD);
+ aic->min_eqd = min(et->rx_coalesce_usecs_low, aic->max_eqd);
+ aic->et_eqd = min(et->rx_coalesce_usecs, aic->max_eqd);
+ aic->et_eqd = max(aic->et_eqd, aic->min_eqd);
+ aic++;
}
return 0;
}
-static void
-be_get_ethtool_stats(struct net_device *netdev,
- struct ethtool_stats *stats, uint64_t *data)
+static void be_get_ethtool_stats(struct net_device *netdev,
+ struct ethtool_stats *stats, uint64_t *data)
{
struct be_adapter *adapter = netdev_priv(netdev);
struct be_rx_obj *rxo;
@@ -350,10 +355,10 @@ be_get_ethtool_stats(struct net_device *netdev,
struct be_rx_stats *stats = rx_stats(rxo);
do {
- start = u64_stats_fetch_begin_bh(&stats->sync);
+ start = u64_stats_fetch_begin_irq(&stats->sync);
data[base] = stats->rx_bytes;
data[base + 1] = stats->rx_pkts;
- } while (u64_stats_fetch_retry_bh(&stats->sync, start));
+ } while (u64_stats_fetch_retry_irq(&stats->sync, start));
for (i = 2; i < ETHTOOL_RXSTATS_NUM; i++) {
p = (u8 *)stats + et_rx_stats[i].offset;
@@ -366,26 +371,25 @@ be_get_ethtool_stats(struct net_device *netdev,
struct be_tx_stats *stats = tx_stats(txo);
do {
- start = u64_stats_fetch_begin_bh(&stats->sync_compl);
+ start = u64_stats_fetch_begin_irq(&stats->sync_compl);
data[base] = stats->tx_compl;
- } while (u64_stats_fetch_retry_bh(&stats->sync_compl, start));
+ } while (u64_stats_fetch_retry_irq(&stats->sync_compl, start));
do {
- start = u64_stats_fetch_begin_bh(&stats->sync);
+ start = u64_stats_fetch_begin_irq(&stats->sync);
for (i = 1; i < ETHTOOL_TXSTATS_NUM; i++) {
p = (u8 *)stats + et_tx_stats[i].offset;
data[base + i] =
(et_tx_stats[i].size == sizeof(u64)) ?
*(u64 *)p : *(u32 *)p;
}
- } while (u64_stats_fetch_retry_bh(&stats->sync, start));
+ } while (u64_stats_fetch_retry_irq(&stats->sync, start));
base += ETHTOOL_TXSTATS_NUM;
}
}
-static void
-be_get_stat_strings(struct net_device *netdev, uint32_t stringset,
- uint8_t *data)
+static void be_get_stat_strings(struct net_device *netdev, uint32_t stringset,
+ uint8_t *data)
{
struct be_adapter *adapter = netdev_priv(netdev);
int i, j;
@@ -635,16 +639,15 @@ be_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *ecmd)
adapter->rx_fc = ecmd->rx_pause;
status = be_cmd_set_flow_control(adapter,
- adapter->tx_fc, adapter->rx_fc);
+ adapter->tx_fc, adapter->rx_fc);
if (status)
dev_warn(&adapter->pdev->dev, "Pause param set failed.\n");
return status;
}
-static int
-be_set_phys_id(struct net_device *netdev,
- enum ethtool_phys_id_state state)
+static int be_set_phys_id(struct net_device *netdev,
+ enum ethtool_phys_id_state state)
{
struct be_adapter *adapter = netdev_priv(netdev);
@@ -672,43 +675,70 @@ be_set_phys_id(struct net_device *netdev,
return 0;
}
+static int be_set_dump(struct net_device *netdev, struct ethtool_dump *dump)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+ struct device *dev = &adapter->pdev->dev;
+ int status;
+
+ if (!lancer_chip(adapter)) {
+ dev_err(dev, "FW dump not supported\n");
+ return -EOPNOTSUPP;
+ }
-static void
-be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+ if (dump_present(adapter)) {
+ dev_err(dev, "Previous dump not cleared, not forcing dump\n");
+ return 0;
+ }
+
+ switch (dump->flag) {
+ case LANCER_INITIATE_FW_DUMP:
+ status = lancer_initiate_dump(adapter);
+ if (!status)
+ dev_info(dev, "F/w dump initiated successfully\n");
+ break;
+ default:
+ dev_err(dev, "Invalid dump level: 0x%x\n", dump->flag);
+ return -EINVAL;
+ }
+ return status;
+}
+
+static void be_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{
struct be_adapter *adapter = netdev_priv(netdev);
- if (be_is_wol_supported(adapter)) {
+ if (adapter->wol_cap & BE_WOL_CAP) {
wol->supported |= WAKE_MAGIC;
- wol->wolopts |= WAKE_MAGIC;
- } else
+ if (adapter->wol_en)
+ wol->wolopts |= WAKE_MAGIC;
+ } else {
wol->wolopts = 0;
+ }
memset(&wol->sopass, 0, sizeof(wol->sopass));
}
-static int
-be_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
+static int be_set_wol(struct net_device *netdev, struct ethtool_wolinfo *wol)
{
struct be_adapter *adapter = netdev_priv(netdev);
if (wol->wolopts & ~WAKE_MAGIC)
return -EOPNOTSUPP;
- if (!be_is_wol_supported(adapter)) {
+ if (!(adapter->wol_cap & BE_WOL_CAP)) {
dev_warn(&adapter->pdev->dev, "WOL not supported\n");
return -EOPNOTSUPP;
}
if (wol->wolopts & WAKE_MAGIC)
- adapter->wol = true;
+ adapter->wol_en = true;
else
- adapter->wol = false;
+ adapter->wol_en = false;
return 0;
}
-static int
-be_test_ddr_dma(struct be_adapter *adapter)
+static int be_test_ddr_dma(struct be_adapter *adapter)
{
int ret, i;
struct be_dma_mem ddrdma_cmd;
@@ -719,14 +749,12 @@ be_test_ddr_dma(struct be_adapter *adapter)
ddrdma_cmd.size = sizeof(struct be_cmd_req_ddrdma_test);
ddrdma_cmd.va = dma_alloc_coherent(&adapter->pdev->dev, ddrdma_cmd.size,
&ddrdma_cmd.dma, GFP_KERNEL);
- if (!ddrdma_cmd.va) {
- dev_err(&adapter->pdev->dev, "Memory allocation failure\n");
+ if (!ddrdma_cmd.va)
return -ENOMEM;
- }
for (i = 0; i < 2; i++) {
ret = be_cmd_ddr_dma_test(adapter, pattern[i],
- 4096, &ddrdma_cmd);
+ 4096, &ddrdma_cmd);
if (ret != 0)
goto err;
}
@@ -738,39 +766,42 @@ err:
}
static u64 be_loopback_test(struct be_adapter *adapter, u8 loopback_type,
- u64 *status)
+ u64 *status)
{
- be_cmd_set_loopback(adapter, adapter->hba_port_num,
- loopback_type, 1);
+ be_cmd_set_loopback(adapter, adapter->hba_port_num, loopback_type, 1);
*status = be_cmd_loopback_test(adapter, adapter->hba_port_num,
- loopback_type, 1500,
- 2, 0xabc);
- be_cmd_set_loopback(adapter, adapter->hba_port_num,
- BE_NO_LOOPBACK, 1);
+ loopback_type, 1500, 2, 0xabc);
+ be_cmd_set_loopback(adapter, adapter->hba_port_num, BE_NO_LOOPBACK, 1);
return *status;
}
-static void
-be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data)
+static void be_self_test(struct net_device *netdev, struct ethtool_test *test,
+ u64 *data)
{
struct be_adapter *adapter = netdev_priv(netdev);
int status;
u8 link_status = 0;
+ if (adapter->function_caps & BE_FUNCTION_CAPS_SUPER_NIC) {
+ dev_err(&adapter->pdev->dev, "Self test not supported\n");
+ test->flags |= ETH_TEST_FL_FAILED;
+ return;
+ }
+
memset(data, 0, sizeof(u64) * ETHTOOL_TESTS_NUM);
if (test->flags & ETH_TEST_FL_OFFLINE) {
- if (be_loopback_test(adapter, BE_MAC_LOOPBACK,
- &data[0]) != 0) {
- test->flags |= ETH_TEST_FL_FAILED;
- }
- if (be_loopback_test(adapter, BE_PHY_LOOPBACK,
- &data[1]) != 0) {
+ if (be_loopback_test(adapter, BE_MAC_LOOPBACK, &data[0]) != 0)
test->flags |= ETH_TEST_FL_FAILED;
- }
- if (be_loopback_test(adapter, BE_ONE_PORT_EXT_LOOPBACK,
- &data[2]) != 0) {
+
+ if (be_loopback_test(adapter, BE_PHY_LOOPBACK, &data[1]) != 0)
test->flags |= ETH_TEST_FL_FAILED;
+
+ if (test->flags & ETH_TEST_FL_EXTERNAL_LB) {
+ if (be_loopback_test(adapter, BE_ONE_PORT_EXT_LOOPBACK,
+ &data[2]) != 0)
+ test->flags |= ETH_TEST_FL_FAILED;
+ test->flags |= ETH_TEST_FL_EXTERNAL_LB_DONE;
}
}
@@ -789,16 +820,14 @@ be_self_test(struct net_device *netdev, struct ethtool_test *test, u64 *data)
}
}
-static int
-be_do_flash(struct net_device *netdev, struct ethtool_flash *efl)
+static int be_do_flash(struct net_device *netdev, struct ethtool_flash *efl)
{
struct be_adapter *adapter = netdev_priv(netdev);
return be_load_fw(adapter, efl->data);
}
-static int
-be_get_eeprom_len(struct net_device *netdev)
+static int be_get_eeprom_len(struct net_device *netdev)
{
struct be_adapter *adapter = netdev_priv(netdev);
@@ -808,18 +837,17 @@ be_get_eeprom_len(struct net_device *netdev)
if (lancer_chip(adapter)) {
if (be_physfn(adapter))
return lancer_cmd_get_file_len(adapter,
- LANCER_VPD_PF_FILE);
+ LANCER_VPD_PF_FILE);
else
return lancer_cmd_get_file_len(adapter,
- LANCER_VPD_VF_FILE);
+ LANCER_VPD_VF_FILE);
} else {
return BE_READ_SEEPROM_LEN;
}
}
-static int
-be_read_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
- uint8_t *data)
+static int be_read_eeprom(struct net_device *netdev,
+ struct ethtool_eeprom *eeprom, uint8_t *data)
{
struct be_adapter *adapter = netdev_priv(netdev);
struct be_dma_mem eeprom_cmd;
@@ -832,10 +860,10 @@ be_read_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
if (lancer_chip(adapter)) {
if (be_physfn(adapter))
return lancer_cmd_read_file(adapter, LANCER_VPD_PF_FILE,
- eeprom->len, data);
+ eeprom->len, data);
else
return lancer_cmd_read_file(adapter, LANCER_VPD_VF_FILE,
- eeprom->len, data);
+ eeprom->len, data);
}
eeprom->magic = BE_VENDOR_ID | (adapter->pdev->device<<16);
@@ -845,11 +873,8 @@ be_read_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
eeprom_cmd.va = dma_alloc_coherent(&adapter->pdev->dev, eeprom_cmd.size,
&eeprom_cmd.dma, GFP_KERNEL);
- if (!eeprom_cmd.va) {
- dev_err(&adapter->pdev->dev,
- "Memory allocation failure. Could not read eeprom\n");
+ if (!eeprom_cmd.va)
return -ENOMEM;
- }
status = be_cmd_get_seeprom_data(adapter, &eeprom_cmd);
@@ -867,76 +892,265 @@ static u32 be_get_msg_level(struct net_device *netdev)
{
struct be_adapter *adapter = netdev_priv(netdev);
- if (lancer_chip(adapter)) {
- dev_err(&adapter->pdev->dev, "Operation not supported\n");
- return -EOPNOTSUPP;
+ return adapter->msg_enable;
+}
+
+static void be_set_msg_level(struct net_device *netdev, u32 level)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+
+ if (adapter->msg_enable == level)
+ return;
+
+ if ((level & NETIF_MSG_HW) != (adapter->msg_enable & NETIF_MSG_HW))
+ if (BEx_chip(adapter))
+ be_cmd_set_fw_log_level(adapter, level & NETIF_MSG_HW ?
+ FW_LOG_LEVEL_DEFAULT :
+ FW_LOG_LEVEL_FATAL);
+ adapter->msg_enable = level;
+
+ return;
+}
+
+static u64 be_get_rss_hash_opts(struct be_adapter *adapter, u64 flow_type)
+{
+ u64 data = 0;
+
+ switch (flow_type) {
+ case TCP_V4_FLOW:
+ if (adapter->rss_info.rss_flags & RSS_ENABLE_IPV4)
+ data |= RXH_IP_DST | RXH_IP_SRC;
+ if (adapter->rss_info.rss_flags & RSS_ENABLE_TCP_IPV4)
+ data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ break;
+ case UDP_V4_FLOW:
+ if (adapter->rss_info.rss_flags & RSS_ENABLE_IPV4)
+ data |= RXH_IP_DST | RXH_IP_SRC;
+ if (adapter->rss_info.rss_flags & RSS_ENABLE_UDP_IPV4)
+ data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ break;
+ case TCP_V6_FLOW:
+ if (adapter->rss_info.rss_flags & RSS_ENABLE_IPV6)
+ data |= RXH_IP_DST | RXH_IP_SRC;
+ if (adapter->rss_info.rss_flags & RSS_ENABLE_TCP_IPV6)
+ data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ break;
+ case UDP_V6_FLOW:
+ if (adapter->rss_info.rss_flags & RSS_ENABLE_IPV6)
+ data |= RXH_IP_DST | RXH_IP_SRC;
+ if (adapter->rss_info.rss_flags & RSS_ENABLE_UDP_IPV6)
+ data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
+ break;
}
- return adapter->msg_enable;
+ return data;
}
-static void be_set_fw_log_level(struct be_adapter *adapter, u32 level)
+static int be_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
+ u32 *rule_locs)
{
- struct be_dma_mem extfat_cmd;
- struct be_fat_conf_params *cfgs;
- int status;
- int i, j;
+ struct be_adapter *adapter = netdev_priv(netdev);
- memset(&extfat_cmd, 0, sizeof(struct be_dma_mem));
- extfat_cmd.size = sizeof(struct be_cmd_resp_get_ext_fat_caps);
- extfat_cmd.va = pci_alloc_consistent(adapter->pdev, extfat_cmd.size,
- &extfat_cmd.dma);
- if (!extfat_cmd.va) {
- dev_err(&adapter->pdev->dev, "%s: Memory allocation failure\n",
- __func__);
- goto err;
+ if (!be_multi_rxq(adapter)) {
+ dev_info(&adapter->pdev->dev,
+ "ethtool::get_rxnfc: RX flow hashing is disabled\n");
+ return -EINVAL;
}
- status = be_cmd_get_ext_fat_capabilites(adapter, &extfat_cmd);
- if (!status) {
- cfgs = (struct be_fat_conf_params *)(extfat_cmd.va +
- sizeof(struct be_cmd_resp_hdr));
- for (i = 0; i < le32_to_cpu(cfgs->num_modules); i++) {
- u32 num_modes = le32_to_cpu(cfgs->module[i].num_modes);
- for (j = 0; j < num_modes; j++) {
- if (cfgs->module[i].trace_lvl[j].mode ==
- MODE_UART)
- cfgs->module[i].trace_lvl[j].dbg_lvl =
- cpu_to_le32(level);
+
+ switch (cmd->cmd) {
+ case ETHTOOL_GRXFH:
+ cmd->data = be_get_rss_hash_opts(adapter, cmd->flow_type);
+ break;
+ case ETHTOOL_GRXRINGS:
+ cmd->data = adapter->num_rx_qs - 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int be_set_rss_hash_opts(struct be_adapter *adapter,
+ struct ethtool_rxnfc *cmd)
+{
+ struct be_rx_obj *rxo;
+ int status = 0, i, j;
+ u8 rsstable[128];
+ u32 rss_flags = adapter->rss_info.rss_flags;
+
+ if (cmd->data != L3_RSS_FLAGS &&
+ cmd->data != (L3_RSS_FLAGS | L4_RSS_FLAGS))
+ return -EINVAL;
+
+ switch (cmd->flow_type) {
+ case TCP_V4_FLOW:
+ if (cmd->data == L3_RSS_FLAGS)
+ rss_flags &= ~RSS_ENABLE_TCP_IPV4;
+ else if (cmd->data == (L3_RSS_FLAGS | L4_RSS_FLAGS))
+ rss_flags |= RSS_ENABLE_IPV4 |
+ RSS_ENABLE_TCP_IPV4;
+ break;
+ case TCP_V6_FLOW:
+ if (cmd->data == L3_RSS_FLAGS)
+ rss_flags &= ~RSS_ENABLE_TCP_IPV6;
+ else if (cmd->data == (L3_RSS_FLAGS | L4_RSS_FLAGS))
+ rss_flags |= RSS_ENABLE_IPV6 |
+ RSS_ENABLE_TCP_IPV6;
+ break;
+ case UDP_V4_FLOW:
+ if ((cmd->data == (L3_RSS_FLAGS | L4_RSS_FLAGS)) &&
+ BEx_chip(adapter))
+ return -EINVAL;
+
+ if (cmd->data == L3_RSS_FLAGS)
+ rss_flags &= ~RSS_ENABLE_UDP_IPV4;
+ else if (cmd->data == (L3_RSS_FLAGS | L4_RSS_FLAGS))
+ rss_flags |= RSS_ENABLE_IPV4 |
+ RSS_ENABLE_UDP_IPV4;
+ break;
+ case UDP_V6_FLOW:
+ if ((cmd->data == (L3_RSS_FLAGS | L4_RSS_FLAGS)) &&
+ BEx_chip(adapter))
+ return -EINVAL;
+
+ if (cmd->data == L3_RSS_FLAGS)
+ rss_flags &= ~RSS_ENABLE_UDP_IPV6;
+ else if (cmd->data == (L3_RSS_FLAGS | L4_RSS_FLAGS))
+ rss_flags |= RSS_ENABLE_IPV6 |
+ RSS_ENABLE_UDP_IPV6;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (rss_flags == adapter->rss_info.rss_flags)
+ return status;
+
+ if (be_multi_rxq(adapter)) {
+ for (j = 0; j < 128; j += adapter->num_rx_qs - 1) {
+ for_all_rss_queues(adapter, rxo, i) {
+ if ((j + i) >= 128)
+ break;
+ rsstable[j + i] = rxo->rss_id;
}
}
- status = be_cmd_set_ext_fat_capabilites(adapter, &extfat_cmd,
- cfgs);
- if (status)
- dev_err(&adapter->pdev->dev,
- "Message level set failed\n");
- } else {
- dev_err(&adapter->pdev->dev, "Message level get failed\n");
}
- pci_free_consistent(adapter->pdev, extfat_cmd.size, extfat_cmd.va,
- extfat_cmd.dma);
-err:
- return;
+ status = be_cmd_rss_config(adapter, adapter->rss_info.rsstable,
+ rss_flags, 128, adapter->rss_info.rss_hkey);
+ if (!status)
+ adapter->rss_info.rss_flags = rss_flags;
+
+ return status;
}
-static void be_set_msg_level(struct net_device *netdev, u32 level)
+static int be_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
{
struct be_adapter *adapter = netdev_priv(netdev);
+ int status = 0;
- if (lancer_chip(adapter)) {
- dev_err(&adapter->pdev->dev, "Operation not supported\n");
- return;
+ if (!be_multi_rxq(adapter)) {
+ dev_err(&adapter->pdev->dev,
+ "ethtool::set_rxnfc: RX flow hashing is disabled\n");
+ return -EINVAL;
}
- if (adapter->msg_enable == level)
- return;
+ switch (cmd->cmd) {
+ case ETHTOOL_SRXFH:
+ status = be_set_rss_hash_opts(adapter, cmd);
+ break;
+ default:
+ return -EINVAL;
+ }
- if ((level & NETIF_MSG_HW) != (adapter->msg_enable & NETIF_MSG_HW))
- be_set_fw_log_level(adapter, level & NETIF_MSG_HW ?
- FW_LOG_LEVEL_DEFAULT : FW_LOG_LEVEL_FATAL);
- adapter->msg_enable = level;
+ return status;
+}
- return;
+static void be_get_channels(struct net_device *netdev,
+ struct ethtool_channels *ch)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+
+ ch->combined_count = adapter->num_evt_qs;
+ ch->max_combined = be_max_qs(adapter);
+}
+
+static int be_set_channels(struct net_device *netdev,
+ struct ethtool_channels *ch)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+
+ if (ch->rx_count || ch->tx_count || ch->other_count ||
+ !ch->combined_count || ch->combined_count > be_max_qs(adapter))
+ return -EINVAL;
+
+ adapter->cfg_num_qs = ch->combined_count;
+
+ return be_update_queues(adapter);
+}
+
+static u32 be_get_rxfh_indir_size(struct net_device *netdev)
+{
+ return RSS_INDIR_TABLE_LEN;
+}
+
+static u32 be_get_rxfh_key_size(struct net_device *netdev)
+{
+ return RSS_HASH_KEY_LEN;
+}
+
+static int be_get_rxfh(struct net_device *netdev, u32 *indir, u8 *hkey)
+{
+ struct be_adapter *adapter = netdev_priv(netdev);
+ int i;
+ struct rss_info *rss = &adapter->rss_info;
+
+ if (indir) {
+ for (i = 0; i < RSS_INDIR_TABLE_LEN; i++)
+ indir[i] = rss->rss_queue[i];
+ }
+
+ if (hkey)
+ memcpy(hkey, rss->rss_hkey, RSS_HASH_KEY_LEN);
+
+ return 0;
+}
+
+static int be_set_rxfh(struct net_device *netdev, const u32 *indir,
+ const u8 *hkey)
+{
+ int rc = 0, i, j;
+ struct be_adapter *adapter = netdev_priv(netdev);
+ u8 rsstable[RSS_INDIR_TABLE_LEN];
+
+ if (indir) {
+ struct be_rx_obj *rxo;
+ for (i = 0; i < RSS_INDIR_TABLE_LEN; i++) {
+ j = indir[i];
+ rxo = &adapter->rx_obj[j];
+ rsstable[i] = rxo->rss_id;
+ adapter->rss_info.rss_queue[i] = j;
+ }
+ } else {
+ memcpy(rsstable, adapter->rss_info.rsstable,
+ RSS_INDIR_TABLE_LEN);
+ }
+
+ if (!hkey)
+ hkey = adapter->rss_info.rss_hkey;
+
+ rc = be_cmd_rss_config(adapter, rsstable,
+ adapter->rss_info.rss_flags,
+ RSS_INDIR_TABLE_LEN, hkey);
+ if (rc) {
+ adapter->rss_info.rss_flags = RSS_ENABLE_NONE;
+ return -EIO;
+ }
+ memcpy(adapter->rss_info.rss_hkey, hkey, RSS_HASH_KEY_LEN);
+ memcpy(adapter->rss_info.rsstable, rsstable,
+ RSS_INDIR_TABLE_LEN);
+ return 0;
}
const struct ethtool_ops be_ethtool_ops = {
@@ -954,6 +1168,7 @@ const struct ethtool_ops be_ethtool_ops = {
.set_pauseparam = be_set_pauseparam,
.get_strings = be_get_stat_strings,
.set_phys_id = be_set_phys_id,
+ .set_dump = be_set_dump,
.get_msglevel = be_get_msg_level,
.set_msglevel = be_set_msg_level,
.get_sset_count = be_get_sset_count,
@@ -962,4 +1177,12 @@ const struct ethtool_ops be_ethtool_ops = {
.get_regs = be_get_regs,
.flash_device = be_do_flash,
.self_test = be_self_test,
+ .get_rxnfc = be_get_rxnfc,
+ .set_rxnfc = be_set_rxnfc,
+ .get_rxfh_indir_size = be_get_rxfh_indir_size,
+ .get_rxfh_key_size = be_get_rxfh_key_size,
+ .get_rxfh = be_get_rxfh,
+ .set_rxfh = be_set_rxfh,
+ .get_channels = be_get_channels,
+ .set_channels = be_set_channels
};