linux_dsm_epyc7002/drivers/net/ethernet/qlogic/qede/qede_ethtool.c
Yuval Mintz fe7cd2bfda qed*: Conditions for changing link
There's some inconsistency in current logic determining whether the
link settings of a given interface can be changed; I.e., in all modes
other than the so-called `deault' mode the interfaces are forbidden from
changing the configuration - but even this rule is not applied to all
user APIs that may change the configuration.

Instead, let the core-module [qed] decide whether an interface can change
the configuration by supporting a new API function. We also revise the
current rule, allowing all interfaces to change their configurations while
laying the infrastructure for future modes where an interface would be
blocked from making such a configuration.

Signed-off-by: Yuval Mintz <Yuval.Mintz@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2016-04-25 15:59:16 -04:00

861 lines
23 KiB
C

/* QLogic qede NIC Driver
* Copyright (c) 2015 QLogic Corporation
*
* This software is available under the terms of the GNU General Public License
* (GPL) Version 2, available from the file COPYING in the main directory of
* this source tree.
*/
#include <linux/version.h>
#include <linux/types.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/string.h>
#include <linux/pci.h>
#include <linux/capability.h>
#include "qede.h"
#define QEDE_STAT_OFFSET(stat_name) (offsetof(struct qede_stats, stat_name))
#define QEDE_STAT_STRING(stat_name) (#stat_name)
#define _QEDE_STAT(stat_name, pf_only) \
{QEDE_STAT_OFFSET(stat_name), QEDE_STAT_STRING(stat_name), pf_only}
#define QEDE_PF_STAT(stat_name) _QEDE_STAT(stat_name, true)
#define QEDE_STAT(stat_name) _QEDE_STAT(stat_name, false)
#define QEDE_RQSTAT_OFFSET(stat_name) \
(offsetof(struct qede_rx_queue, stat_name))
#define QEDE_RQSTAT_STRING(stat_name) (#stat_name)
#define QEDE_RQSTAT(stat_name) \
{QEDE_RQSTAT_OFFSET(stat_name), QEDE_RQSTAT_STRING(stat_name)}
static const struct {
u64 offset;
char string[ETH_GSTRING_LEN];
} qede_rqstats_arr[] = {
QEDE_RQSTAT(rx_hw_errors),
QEDE_RQSTAT(rx_alloc_errors),
};
#define QEDE_NUM_RQSTATS ARRAY_SIZE(qede_rqstats_arr)
#define QEDE_RQSTATS_DATA(dev, sindex, rqindex) \
(*((u64 *)(((char *)(dev->fp_array[(rqindex)].rxq)) +\
qede_rqstats_arr[(sindex)].offset)))
static const struct {
u64 offset;
char string[ETH_GSTRING_LEN];
bool pf_only;
} qede_stats_arr[] = {
QEDE_STAT(rx_ucast_bytes),
QEDE_STAT(rx_mcast_bytes),
QEDE_STAT(rx_bcast_bytes),
QEDE_STAT(rx_ucast_pkts),
QEDE_STAT(rx_mcast_pkts),
QEDE_STAT(rx_bcast_pkts),
QEDE_STAT(tx_ucast_bytes),
QEDE_STAT(tx_mcast_bytes),
QEDE_STAT(tx_bcast_bytes),
QEDE_STAT(tx_ucast_pkts),
QEDE_STAT(tx_mcast_pkts),
QEDE_STAT(tx_bcast_pkts),
QEDE_PF_STAT(rx_64_byte_packets),
QEDE_PF_STAT(rx_65_to_127_byte_packets),
QEDE_PF_STAT(rx_128_to_255_byte_packets),
QEDE_PF_STAT(rx_256_to_511_byte_packets),
QEDE_PF_STAT(rx_512_to_1023_byte_packets),
QEDE_PF_STAT(rx_1024_to_1518_byte_packets),
QEDE_PF_STAT(rx_1519_to_1522_byte_packets),
QEDE_PF_STAT(rx_1519_to_2047_byte_packets),
QEDE_PF_STAT(rx_2048_to_4095_byte_packets),
QEDE_PF_STAT(rx_4096_to_9216_byte_packets),
QEDE_PF_STAT(rx_9217_to_16383_byte_packets),
QEDE_PF_STAT(tx_64_byte_packets),
QEDE_PF_STAT(tx_65_to_127_byte_packets),
QEDE_PF_STAT(tx_128_to_255_byte_packets),
QEDE_PF_STAT(tx_256_to_511_byte_packets),
QEDE_PF_STAT(tx_512_to_1023_byte_packets),
QEDE_PF_STAT(tx_1024_to_1518_byte_packets),
QEDE_PF_STAT(tx_1519_to_2047_byte_packets),
QEDE_PF_STAT(tx_2048_to_4095_byte_packets),
QEDE_PF_STAT(tx_4096_to_9216_byte_packets),
QEDE_PF_STAT(tx_9217_to_16383_byte_packets),
QEDE_PF_STAT(rx_mac_crtl_frames),
QEDE_PF_STAT(tx_mac_ctrl_frames),
QEDE_PF_STAT(rx_pause_frames),
QEDE_PF_STAT(tx_pause_frames),
QEDE_PF_STAT(rx_pfc_frames),
QEDE_PF_STAT(tx_pfc_frames),
QEDE_PF_STAT(rx_crc_errors),
QEDE_PF_STAT(rx_align_errors),
QEDE_PF_STAT(rx_carrier_errors),
QEDE_PF_STAT(rx_oversize_packets),
QEDE_PF_STAT(rx_jabbers),
QEDE_PF_STAT(rx_undersize_packets),
QEDE_PF_STAT(rx_fragments),
QEDE_PF_STAT(tx_lpi_entry_count),
QEDE_PF_STAT(tx_total_collisions),
QEDE_PF_STAT(brb_truncates),
QEDE_PF_STAT(brb_discards),
QEDE_STAT(no_buff_discards),
QEDE_PF_STAT(mftag_filter_discards),
QEDE_PF_STAT(mac_filter_discards),
QEDE_STAT(tx_err_drop_pkts),
QEDE_STAT(coalesced_pkts),
QEDE_STAT(coalesced_events),
QEDE_STAT(coalesced_aborts_num),
QEDE_STAT(non_coalesced_pkts),
QEDE_STAT(coalesced_bytes),
};
#define QEDE_STATS_DATA(dev, index) \
(*((u64 *)(((char *)(dev)) + offsetof(struct qede_dev, stats) \
+ qede_stats_arr[(index)].offset)))
#define QEDE_NUM_STATS ARRAY_SIZE(qede_stats_arr)
enum {
QEDE_PRI_FLAG_CMT,
QEDE_PRI_FLAG_LEN,
};
static const char qede_private_arr[QEDE_PRI_FLAG_LEN][ETH_GSTRING_LEN] = {
"Coupled-Function",
};
static void qede_get_strings_stats(struct qede_dev *edev, u8 *buf)
{
int i, j, k;
for (i = 0, j = 0; i < QEDE_NUM_STATS; i++) {
strcpy(buf + j * ETH_GSTRING_LEN,
qede_stats_arr[i].string);
j++;
}
for (k = 0; k < QEDE_NUM_RQSTATS; k++, j++)
strcpy(buf + j * ETH_GSTRING_LEN,
qede_rqstats_arr[k].string);
}
static void qede_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
{
struct qede_dev *edev = netdev_priv(dev);
switch (stringset) {
case ETH_SS_STATS:
qede_get_strings_stats(edev, buf);
break;
case ETH_SS_PRIV_FLAGS:
memcpy(buf, qede_private_arr,
ETH_GSTRING_LEN * QEDE_PRI_FLAG_LEN);
break;
default:
DP_VERBOSE(edev, QED_MSG_DEBUG,
"Unsupported stringset 0x%08x\n", stringset);
}
}
static void qede_get_ethtool_stats(struct net_device *dev,
struct ethtool_stats *stats, u64 *buf)
{
struct qede_dev *edev = netdev_priv(dev);
int sidx, cnt = 0;
int qid;
qede_fill_by_demand_stats(edev);
mutex_lock(&edev->qede_lock);
for (sidx = 0; sidx < QEDE_NUM_STATS; sidx++)
buf[cnt++] = QEDE_STATS_DATA(edev, sidx);
for (sidx = 0; sidx < QEDE_NUM_RQSTATS; sidx++) {
buf[cnt] = 0;
for (qid = 0; qid < edev->num_rss; qid++)
buf[cnt] += QEDE_RQSTATS_DATA(edev, sidx, qid);
cnt++;
}
mutex_unlock(&edev->qede_lock);
}
static int qede_get_sset_count(struct net_device *dev, int stringset)
{
struct qede_dev *edev = netdev_priv(dev);
int num_stats = QEDE_NUM_STATS;
switch (stringset) {
case ETH_SS_STATS:
return num_stats + QEDE_NUM_RQSTATS;
case ETH_SS_PRIV_FLAGS:
return QEDE_PRI_FLAG_LEN;
default:
DP_VERBOSE(edev, QED_MSG_DEBUG,
"Unsupported stringset 0x%08x\n", stringset);
return -EINVAL;
}
}
static u32 qede_get_priv_flags(struct net_device *dev)
{
struct qede_dev *edev = netdev_priv(dev);
return (!!(edev->dev_info.common.num_hwfns > 1)) << QEDE_PRI_FLAG_CMT;
}
static int qede_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct qede_dev *edev = netdev_priv(dev);
struct qed_link_output current_link;
memset(&current_link, 0, sizeof(current_link));
edev->ops->common->get_link(edev->cdev, &current_link);
cmd->supported = current_link.supported_caps;
cmd->advertising = current_link.advertised_caps;
if ((edev->state == QEDE_STATE_OPEN) && (current_link.link_up)) {
ethtool_cmd_speed_set(cmd, current_link.speed);
cmd->duplex = current_link.duplex;
} else {
cmd->duplex = DUPLEX_UNKNOWN;
ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
}
cmd->port = current_link.port;
cmd->autoneg = (current_link.autoneg) ? AUTONEG_ENABLE :
AUTONEG_DISABLE;
cmd->lp_advertising = current_link.lp_caps;
return 0;
}
static int qede_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct qede_dev *edev = netdev_priv(dev);
struct qed_link_output current_link;
struct qed_link_params params;
u32 speed;
if (!edev->ops || !edev->ops->common->can_link_change(edev->cdev)) {
DP_INFO(edev,
"Link settings are not allowed to be changed\n");
return -EOPNOTSUPP;
}
memset(&current_link, 0, sizeof(current_link));
memset(&params, 0, sizeof(params));
edev->ops->common->get_link(edev->cdev, &current_link);
speed = ethtool_cmd_speed(cmd);
params.override_flags |= QED_LINK_OVERRIDE_SPEED_ADV_SPEEDS;
params.override_flags |= QED_LINK_OVERRIDE_SPEED_AUTONEG;
if (cmd->autoneg == AUTONEG_ENABLE) {
params.autoneg = true;
params.forced_speed = 0;
params.adv_speeds = cmd->advertising;
} else { /* forced speed */
params.override_flags |= QED_LINK_OVERRIDE_SPEED_FORCED_SPEED;
params.autoneg = false;
params.forced_speed = speed;
switch (speed) {
case SPEED_10000:
if (!(current_link.supported_caps &
SUPPORTED_10000baseKR_Full)) {
DP_INFO(edev, "10G speed not supported\n");
return -EINVAL;
}
params.adv_speeds = SUPPORTED_10000baseKR_Full;
break;
case SPEED_40000:
if (!(current_link.supported_caps &
SUPPORTED_40000baseLR4_Full)) {
DP_INFO(edev, "40G speed not supported\n");
return -EINVAL;
}
params.adv_speeds = SUPPORTED_40000baseLR4_Full;
break;
default:
DP_INFO(edev, "Unsupported speed %u\n", speed);
return -EINVAL;
}
}
params.link_up = true;
edev->ops->common->set_link(edev->cdev, &params);
return 0;
}
static void qede_get_drvinfo(struct net_device *ndev,
struct ethtool_drvinfo *info)
{
char mfw[ETHTOOL_FWVERS_LEN], storm[ETHTOOL_FWVERS_LEN];
struct qede_dev *edev = netdev_priv(ndev);
strlcpy(info->driver, "qede", sizeof(info->driver));
strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version));
snprintf(storm, ETHTOOL_FWVERS_LEN, "%d.%d.%d.%d",
edev->dev_info.common.fw_major,
edev->dev_info.common.fw_minor,
edev->dev_info.common.fw_rev,
edev->dev_info.common.fw_eng);
snprintf(mfw, ETHTOOL_FWVERS_LEN, "%d.%d.%d.%d",
(edev->dev_info.common.mfw_rev >> 24) & 0xFF,
(edev->dev_info.common.mfw_rev >> 16) & 0xFF,
(edev->dev_info.common.mfw_rev >> 8) & 0xFF,
edev->dev_info.common.mfw_rev & 0xFF);
if ((strlen(storm) + strlen(mfw) + strlen("mfw storm ")) <
sizeof(info->fw_version)) {
snprintf(info->fw_version, sizeof(info->fw_version),
"mfw %s storm %s", mfw, storm);
} else {
snprintf(info->fw_version, sizeof(info->fw_version),
"%s %s", mfw, storm);
}
strlcpy(info->bus_info, pci_name(edev->pdev), sizeof(info->bus_info));
}
static u32 qede_get_msglevel(struct net_device *ndev)
{
struct qede_dev *edev = netdev_priv(ndev);
return ((u32)edev->dp_level << QED_LOG_LEVEL_SHIFT) |
edev->dp_module;
}
static void qede_set_msglevel(struct net_device *ndev, u32 level)
{
struct qede_dev *edev = netdev_priv(ndev);
u32 dp_module = 0;
u8 dp_level = 0;
qede_config_debug(level, &dp_module, &dp_level);
edev->dp_level = dp_level;
edev->dp_module = dp_module;
edev->ops->common->update_msglvl(edev->cdev,
dp_module, dp_level);
}
static int qede_nway_reset(struct net_device *dev)
{
struct qede_dev *edev = netdev_priv(dev);
struct qed_link_output current_link;
struct qed_link_params link_params;
if (!edev->ops || !edev->ops->common->can_link_change(edev->cdev)) {
DP_INFO(edev,
"Link settings are not allowed to be changed\n");
return -EOPNOTSUPP;
}
if (!netif_running(dev))
return 0;
memset(&current_link, 0, sizeof(current_link));
edev->ops->common->get_link(edev->cdev, &current_link);
if (!current_link.link_up)
return 0;
/* Toggle the link */
memset(&link_params, 0, sizeof(link_params));
link_params.link_up = false;
edev->ops->common->set_link(edev->cdev, &link_params);
link_params.link_up = true;
edev->ops->common->set_link(edev->cdev, &link_params);
return 0;
}
static u32 qede_get_link(struct net_device *dev)
{
struct qede_dev *edev = netdev_priv(dev);
struct qed_link_output current_link;
memset(&current_link, 0, sizeof(current_link));
edev->ops->common->get_link(edev->cdev, &current_link);
return current_link.link_up;
}
static void qede_get_ringparam(struct net_device *dev,
struct ethtool_ringparam *ering)
{
struct qede_dev *edev = netdev_priv(dev);
ering->rx_max_pending = NUM_RX_BDS_MAX;
ering->rx_pending = edev->q_num_rx_buffers;
ering->tx_max_pending = NUM_TX_BDS_MAX;
ering->tx_pending = edev->q_num_tx_buffers;
}
static int qede_set_ringparam(struct net_device *dev,
struct ethtool_ringparam *ering)
{
struct qede_dev *edev = netdev_priv(dev);
DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
"Set ring params command parameters: rx_pending = %d, tx_pending = %d\n",
ering->rx_pending, ering->tx_pending);
/* Validate legality of configuration */
if (ering->rx_pending > NUM_RX_BDS_MAX ||
ering->rx_pending < NUM_RX_BDS_MIN ||
ering->tx_pending > NUM_TX_BDS_MAX ||
ering->tx_pending < NUM_TX_BDS_MIN) {
DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
"Can only support Rx Buffer size [0%08x,...,0x%08x] and Tx Buffer size [0x%08x,...,0x%08x]\n",
NUM_RX_BDS_MIN, NUM_RX_BDS_MAX,
NUM_TX_BDS_MIN, NUM_TX_BDS_MAX);
return -EINVAL;
}
/* Change ring size and re-load */
edev->q_num_rx_buffers = ering->rx_pending;
edev->q_num_tx_buffers = ering->tx_pending;
if (netif_running(edev->ndev))
qede_reload(edev, NULL, NULL);
return 0;
}
static void qede_get_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
struct qede_dev *edev = netdev_priv(dev);
struct qed_link_output current_link;
memset(&current_link, 0, sizeof(current_link));
edev->ops->common->get_link(edev->cdev, &current_link);
if (current_link.pause_config & QED_LINK_PAUSE_AUTONEG_ENABLE)
epause->autoneg = true;
if (current_link.pause_config & QED_LINK_PAUSE_RX_ENABLE)
epause->rx_pause = true;
if (current_link.pause_config & QED_LINK_PAUSE_TX_ENABLE)
epause->tx_pause = true;
DP_VERBOSE(edev, QED_MSG_DEBUG,
"ethtool_pauseparam: cmd %d autoneg %d rx_pause %d tx_pause %d\n",
epause->cmd, epause->autoneg, epause->rx_pause,
epause->tx_pause);
}
static int qede_set_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
struct qede_dev *edev = netdev_priv(dev);
struct qed_link_params params;
struct qed_link_output current_link;
if (!edev->ops || !edev->ops->common->can_link_change(edev->cdev)) {
DP_INFO(edev,
"Pause settings are not allowed to be changed\n");
return -EOPNOTSUPP;
}
memset(&current_link, 0, sizeof(current_link));
edev->ops->common->get_link(edev->cdev, &current_link);
memset(&params, 0, sizeof(params));
params.override_flags |= QED_LINK_OVERRIDE_PAUSE_CONFIG;
if (epause->autoneg) {
if (!(current_link.supported_caps & SUPPORTED_Autoneg)) {
DP_INFO(edev, "autoneg not supported\n");
return -EINVAL;
}
params.pause_config |= QED_LINK_PAUSE_AUTONEG_ENABLE;
}
if (epause->rx_pause)
params.pause_config |= QED_LINK_PAUSE_RX_ENABLE;
if (epause->tx_pause)
params.pause_config |= QED_LINK_PAUSE_TX_ENABLE;
params.link_up = true;
edev->ops->common->set_link(edev->cdev, &params);
return 0;
}
static void qede_update_mtu(struct qede_dev *edev, union qede_reload_args *args)
{
edev->ndev->mtu = args->mtu;
}
/* Netdevice NDOs */
#define ETH_MAX_JUMBO_PACKET_SIZE 9600
#define ETH_MIN_PACKET_SIZE 60
int qede_change_mtu(struct net_device *ndev, int new_mtu)
{
struct qede_dev *edev = netdev_priv(ndev);
union qede_reload_args args;
if ((new_mtu > ETH_MAX_JUMBO_PACKET_SIZE) ||
((new_mtu + ETH_HLEN) < ETH_MIN_PACKET_SIZE)) {
DP_ERR(edev, "Can't support requested MTU size\n");
return -EINVAL;
}
DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
"Configuring MTU size of %d\n", new_mtu);
/* Set the mtu field and re-start the interface if needed*/
args.mtu = new_mtu;
if (netif_running(edev->ndev))
qede_reload(edev, &qede_update_mtu, &args);
qede_update_mtu(edev, &args);
return 0;
}
static void qede_get_channels(struct net_device *dev,
struct ethtool_channels *channels)
{
struct qede_dev *edev = netdev_priv(dev);
channels->max_combined = QEDE_MAX_RSS_CNT(edev);
channels->combined_count = QEDE_RSS_CNT(edev);
}
static int qede_set_channels(struct net_device *dev,
struct ethtool_channels *channels)
{
struct qede_dev *edev = netdev_priv(dev);
DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
"set-channels command parameters: rx = %d, tx = %d, other = %d, combined = %d\n",
channels->rx_count, channels->tx_count,
channels->other_count, channels->combined_count);
/* We don't support separate rx / tx, nor `other' channels. */
if (channels->rx_count || channels->tx_count ||
channels->other_count || (channels->combined_count == 0) ||
(channels->combined_count > QEDE_MAX_RSS_CNT(edev))) {
DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
"command parameters not supported\n");
return -EINVAL;
}
/* Check if there was a change in the active parameters */
if (channels->combined_count == QEDE_RSS_CNT(edev)) {
DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
"No change in active parameters\n");
return 0;
}
/* We need the number of queues to be divisible between the hwfns */
if (channels->combined_count % edev->dev_info.common.num_hwfns) {
DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
"Number of channels must be divisable by %04x\n",
edev->dev_info.common.num_hwfns);
return -EINVAL;
}
/* Set number of queues and reload if necessary */
edev->req_rss = channels->combined_count;
if (netif_running(dev))
qede_reload(edev, NULL, NULL);
return 0;
}
static int qede_set_phys_id(struct net_device *dev,
enum ethtool_phys_id_state state)
{
struct qede_dev *edev = netdev_priv(dev);
u8 led_state = 0;
switch (state) {
case ETHTOOL_ID_ACTIVE:
return 1; /* cycle on/off once per second */
case ETHTOOL_ID_ON:
led_state = QED_LED_MODE_ON;
break;
case ETHTOOL_ID_OFF:
led_state = QED_LED_MODE_OFF;
break;
case ETHTOOL_ID_INACTIVE:
led_state = QED_LED_MODE_RESTORE;
break;
}
edev->ops->common->set_led(edev->cdev, led_state);
return 0;
}
static int qede_get_rss_flags(struct qede_dev *edev, struct ethtool_rxnfc *info)
{
info->data = RXH_IP_SRC | RXH_IP_DST;
switch (info->flow_type) {
case TCP_V4_FLOW:
case TCP_V6_FLOW:
info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
break;
case UDP_V4_FLOW:
if (edev->rss_params.rss_caps & QED_RSS_IPV4_UDP)
info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
break;
case UDP_V6_FLOW:
if (edev->rss_params.rss_caps & QED_RSS_IPV6_UDP)
info->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3;
break;
case IPV4_FLOW:
case IPV6_FLOW:
break;
default:
info->data = 0;
break;
}
return 0;
}
static int qede_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info,
u32 *rules __always_unused)
{
struct qede_dev *edev = netdev_priv(dev);
switch (info->cmd) {
case ETHTOOL_GRXRINGS:
info->data = edev->num_rss;
return 0;
case ETHTOOL_GRXFH:
return qede_get_rss_flags(edev, info);
default:
DP_ERR(edev, "Command parameters not supported\n");
return -EOPNOTSUPP;
}
}
static int qede_set_rss_flags(struct qede_dev *edev, struct ethtool_rxnfc *info)
{
struct qed_update_vport_params vport_update_params;
u8 set_caps = 0, clr_caps = 0;
DP_VERBOSE(edev, QED_MSG_DEBUG,
"Set rss flags command parameters: flow type = %d, data = %llu\n",
info->flow_type, info->data);
switch (info->flow_type) {
case TCP_V4_FLOW:
case TCP_V6_FLOW:
/* For TCP only 4-tuple hash is supported */
if (info->data ^ (RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
DP_INFO(edev, "Command parameters not supported\n");
return -EINVAL;
}
return 0;
case UDP_V4_FLOW:
/* For UDP either 2-tuple hash or 4-tuple hash is supported */
if (info->data == (RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
set_caps = QED_RSS_IPV4_UDP;
DP_VERBOSE(edev, QED_MSG_DEBUG,
"UDP 4-tuple enabled\n");
} else if (info->data == (RXH_IP_SRC | RXH_IP_DST)) {
clr_caps = QED_RSS_IPV4_UDP;
DP_VERBOSE(edev, QED_MSG_DEBUG,
"UDP 4-tuple disabled\n");
} else {
return -EINVAL;
}
break;
case UDP_V6_FLOW:
/* For UDP either 2-tuple hash or 4-tuple hash is supported */
if (info->data == (RXH_IP_SRC | RXH_IP_DST |
RXH_L4_B_0_1 | RXH_L4_B_2_3)) {
set_caps = QED_RSS_IPV6_UDP;
DP_VERBOSE(edev, QED_MSG_DEBUG,
"UDP 4-tuple enabled\n");
} else if (info->data == (RXH_IP_SRC | RXH_IP_DST)) {
clr_caps = QED_RSS_IPV6_UDP;
DP_VERBOSE(edev, QED_MSG_DEBUG,
"UDP 4-tuple disabled\n");
} else {
return -EINVAL;
}
break;
case IPV4_FLOW:
case IPV6_FLOW:
/* For IP only 2-tuple hash is supported */
if (info->data ^ (RXH_IP_SRC | RXH_IP_DST)) {
DP_INFO(edev, "Command parameters not supported\n");
return -EINVAL;
}
return 0;
case SCTP_V4_FLOW:
case AH_ESP_V4_FLOW:
case AH_V4_FLOW:
case ESP_V4_FLOW:
case SCTP_V6_FLOW:
case AH_ESP_V6_FLOW:
case AH_V6_FLOW:
case ESP_V6_FLOW:
case IP_USER_FLOW:
case ETHER_FLOW:
/* RSS is not supported for these protocols */
if (info->data) {
DP_INFO(edev, "Command parameters not supported\n");
return -EINVAL;
}
return 0;
default:
return -EINVAL;
}
/* No action is needed if there is no change in the rss capability */
if (edev->rss_params.rss_caps == ((edev->rss_params.rss_caps &
~clr_caps) | set_caps))
return 0;
/* Update internal configuration */
edev->rss_params.rss_caps = (edev->rss_params.rss_caps & ~clr_caps) |
set_caps;
edev->rss_params_inited |= QEDE_RSS_CAPS_INITED;
/* Re-configure if possible */
if (netif_running(edev->ndev)) {
memset(&vport_update_params, 0, sizeof(vport_update_params));
vport_update_params.update_rss_flg = 1;
vport_update_params.vport_id = 0;
memcpy(&vport_update_params.rss_params, &edev->rss_params,
sizeof(vport_update_params.rss_params));
return edev->ops->vport_update(edev->cdev,
&vport_update_params);
}
return 0;
}
static int qede_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info)
{
struct qede_dev *edev = netdev_priv(dev);
switch (info->cmd) {
case ETHTOOL_SRXFH:
return qede_set_rss_flags(edev, info);
default:
DP_INFO(edev, "Command parameters not supported\n");
return -EOPNOTSUPP;
}
}
static u32 qede_get_rxfh_indir_size(struct net_device *dev)
{
return QED_RSS_IND_TABLE_SIZE;
}
static u32 qede_get_rxfh_key_size(struct net_device *dev)
{
struct qede_dev *edev = netdev_priv(dev);
return sizeof(edev->rss_params.rss_key);
}
static int qede_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, u8 *hfunc)
{
struct qede_dev *edev = netdev_priv(dev);
int i;
if (hfunc)
*hfunc = ETH_RSS_HASH_TOP;
if (!indir)
return 0;
for (i = 0; i < QED_RSS_IND_TABLE_SIZE; i++)
indir[i] = edev->rss_params.rss_ind_table[i];
if (key)
memcpy(key, edev->rss_params.rss_key,
qede_get_rxfh_key_size(dev));
return 0;
}
static int qede_set_rxfh(struct net_device *dev, const u32 *indir,
const u8 *key, const u8 hfunc)
{
struct qed_update_vport_params vport_update_params;
struct qede_dev *edev = netdev_priv(dev);
int i;
if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
return -EOPNOTSUPP;
if (!indir && !key)
return 0;
if (indir) {
for (i = 0; i < QED_RSS_IND_TABLE_SIZE; i++)
edev->rss_params.rss_ind_table[i] = indir[i];
edev->rss_params_inited |= QEDE_RSS_INDIR_INITED;
}
if (key) {
memcpy(&edev->rss_params.rss_key, key,
qede_get_rxfh_key_size(dev));
edev->rss_params_inited |= QEDE_RSS_KEY_INITED;
}
if (netif_running(edev->ndev)) {
memset(&vport_update_params, 0, sizeof(vport_update_params));
vport_update_params.update_rss_flg = 1;
vport_update_params.vport_id = 0;
memcpy(&vport_update_params.rss_params, &edev->rss_params,
sizeof(vport_update_params.rss_params));
return edev->ops->vport_update(edev->cdev,
&vport_update_params);
}
return 0;
}
static const struct ethtool_ops qede_ethtool_ops = {
.get_settings = qede_get_settings,
.set_settings = qede_set_settings,
.get_drvinfo = qede_get_drvinfo,
.get_msglevel = qede_get_msglevel,
.set_msglevel = qede_set_msglevel,
.nway_reset = qede_nway_reset,
.get_link = qede_get_link,
.get_ringparam = qede_get_ringparam,
.set_ringparam = qede_set_ringparam,
.get_pauseparam = qede_get_pauseparam,
.set_pauseparam = qede_set_pauseparam,
.get_strings = qede_get_strings,
.set_phys_id = qede_set_phys_id,
.get_ethtool_stats = qede_get_ethtool_stats,
.get_priv_flags = qede_get_priv_flags,
.get_sset_count = qede_get_sset_count,
.get_rxnfc = qede_get_rxnfc,
.set_rxnfc = qede_set_rxnfc,
.get_rxfh_indir_size = qede_get_rxfh_indir_size,
.get_rxfh_key_size = qede_get_rxfh_key_size,
.get_rxfh = qede_get_rxfh,
.set_rxfh = qede_set_rxfh,
.get_channels = qede_get_channels,
.set_channels = qede_set_channels,
};
void qede_set_ethtool_ops(struct net_device *dev)
{
dev->ethtool_ops = &qede_ethtool_ops;
}