mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-29 22:14:41 +08:00
37dd348270
Commit6e7333d
"net: add rx_nohandler stat counter" added the new entry rx_nohandler into struct rtnl_link_stats64. Unfortunately the bna driver foolishly depends on the structure. It uses part of it for ethtool statistics and it's not bad but the driver assumes its size is constant as it defines string for each existing entry. The problem occurs when the structure is extended because you need to modify bna driver as well. If not any attempt to retrieve ethtool statistics results in crash in bnad_get_strings(). The patch changes BNAD_ETHTOOL_STATS_NUM so it counts real number of strings in the array and also removes rtnl_link_stats64 entries that are not used in output and are always zero. Fixes:6e7333d
"net: add rx_nohandler stat counter" Signed-off-by: Ivan Vecera <ivecera@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1147 lines
29 KiB
C
1147 lines
29 KiB
C
/*
|
|
* Linux network driver for QLogic BR-series Converged Network Adapter.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License (GPL) Version 2 as
|
|
* published by the Free Software Foundation
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*/
|
|
/*
|
|
* Copyright (c) 2005-2014 Brocade Communications Systems, Inc.
|
|
* Copyright (c) 2014-2015 QLogic Corporation
|
|
* All rights reserved
|
|
* www.qlogic.com
|
|
*/
|
|
|
|
#include "cna.h"
|
|
|
|
#include <linux/netdevice.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/ethtool.h>
|
|
#include <linux/rtnetlink.h>
|
|
|
|
#include "bna.h"
|
|
|
|
#include "bnad.h"
|
|
|
|
#define BNAD_NUM_TXF_COUNTERS 12
|
|
#define BNAD_NUM_RXF_COUNTERS 10
|
|
#define BNAD_NUM_CQ_COUNTERS (3 + 5)
|
|
#define BNAD_NUM_RXQ_COUNTERS 7
|
|
#define BNAD_NUM_TXQ_COUNTERS 5
|
|
|
|
static const char *bnad_net_stats_strings[] = {
|
|
"rx_packets",
|
|
"tx_packets",
|
|
"rx_bytes",
|
|
"tx_bytes",
|
|
"rx_errors",
|
|
"tx_errors",
|
|
"rx_dropped",
|
|
"tx_dropped",
|
|
"multicast",
|
|
"collisions",
|
|
"rx_length_errors",
|
|
"rx_crc_errors",
|
|
"rx_frame_errors",
|
|
"tx_fifo_errors",
|
|
|
|
"netif_queue_stop",
|
|
"netif_queue_wakeup",
|
|
"netif_queue_stopped",
|
|
"tso4",
|
|
"tso6",
|
|
"tso_err",
|
|
"tcpcsum_offload",
|
|
"udpcsum_offload",
|
|
"csum_help",
|
|
"tx_skb_too_short",
|
|
"tx_skb_stopping",
|
|
"tx_skb_max_vectors",
|
|
"tx_skb_mss_too_long",
|
|
"tx_skb_tso_too_short",
|
|
"tx_skb_tso_prepare",
|
|
"tx_skb_non_tso_too_long",
|
|
"tx_skb_tcp_hdr",
|
|
"tx_skb_udp_hdr",
|
|
"tx_skb_csum_err",
|
|
"tx_skb_headlen_too_long",
|
|
"tx_skb_headlen_zero",
|
|
"tx_skb_frag_zero",
|
|
"tx_skb_len_mismatch",
|
|
"tx_skb_map_failed",
|
|
"hw_stats_updates",
|
|
"netif_rx_dropped",
|
|
|
|
"link_toggle",
|
|
"cee_toggle",
|
|
|
|
"rxp_info_alloc_failed",
|
|
"mbox_intr_disabled",
|
|
"mbox_intr_enabled",
|
|
"tx_unmap_q_alloc_failed",
|
|
"rx_unmap_q_alloc_failed",
|
|
"rxbuf_alloc_failed",
|
|
"rxbuf_map_failed",
|
|
|
|
"mac_stats_clr_cnt",
|
|
"mac_frame_64",
|
|
"mac_frame_65_127",
|
|
"mac_frame_128_255",
|
|
"mac_frame_256_511",
|
|
"mac_frame_512_1023",
|
|
"mac_frame_1024_1518",
|
|
"mac_frame_1518_1522",
|
|
"mac_rx_bytes",
|
|
"mac_rx_packets",
|
|
"mac_rx_fcs_error",
|
|
"mac_rx_multicast",
|
|
"mac_rx_broadcast",
|
|
"mac_rx_control_frames",
|
|
"mac_rx_pause",
|
|
"mac_rx_unknown_opcode",
|
|
"mac_rx_alignment_error",
|
|
"mac_rx_frame_length_error",
|
|
"mac_rx_code_error",
|
|
"mac_rx_carrier_sense_error",
|
|
"mac_rx_undersize",
|
|
"mac_rx_oversize",
|
|
"mac_rx_fragments",
|
|
"mac_rx_jabber",
|
|
"mac_rx_drop",
|
|
|
|
"mac_tx_bytes",
|
|
"mac_tx_packets",
|
|
"mac_tx_multicast",
|
|
"mac_tx_broadcast",
|
|
"mac_tx_pause",
|
|
"mac_tx_deferral",
|
|
"mac_tx_excessive_deferral",
|
|
"mac_tx_single_collision",
|
|
"mac_tx_muliple_collision",
|
|
"mac_tx_late_collision",
|
|
"mac_tx_excessive_collision",
|
|
"mac_tx_total_collision",
|
|
"mac_tx_pause_honored",
|
|
"mac_tx_drop",
|
|
"mac_tx_jabber",
|
|
"mac_tx_fcs_error",
|
|
"mac_tx_control_frame",
|
|
"mac_tx_oversize",
|
|
"mac_tx_undersize",
|
|
"mac_tx_fragments",
|
|
|
|
"bpc_tx_pause_0",
|
|
"bpc_tx_pause_1",
|
|
"bpc_tx_pause_2",
|
|
"bpc_tx_pause_3",
|
|
"bpc_tx_pause_4",
|
|
"bpc_tx_pause_5",
|
|
"bpc_tx_pause_6",
|
|
"bpc_tx_pause_7",
|
|
"bpc_tx_zero_pause_0",
|
|
"bpc_tx_zero_pause_1",
|
|
"bpc_tx_zero_pause_2",
|
|
"bpc_tx_zero_pause_3",
|
|
"bpc_tx_zero_pause_4",
|
|
"bpc_tx_zero_pause_5",
|
|
"bpc_tx_zero_pause_6",
|
|
"bpc_tx_zero_pause_7",
|
|
"bpc_tx_first_pause_0",
|
|
"bpc_tx_first_pause_1",
|
|
"bpc_tx_first_pause_2",
|
|
"bpc_tx_first_pause_3",
|
|
"bpc_tx_first_pause_4",
|
|
"bpc_tx_first_pause_5",
|
|
"bpc_tx_first_pause_6",
|
|
"bpc_tx_first_pause_7",
|
|
|
|
"bpc_rx_pause_0",
|
|
"bpc_rx_pause_1",
|
|
"bpc_rx_pause_2",
|
|
"bpc_rx_pause_3",
|
|
"bpc_rx_pause_4",
|
|
"bpc_rx_pause_5",
|
|
"bpc_rx_pause_6",
|
|
"bpc_rx_pause_7",
|
|
"bpc_rx_zero_pause_0",
|
|
"bpc_rx_zero_pause_1",
|
|
"bpc_rx_zero_pause_2",
|
|
"bpc_rx_zero_pause_3",
|
|
"bpc_rx_zero_pause_4",
|
|
"bpc_rx_zero_pause_5",
|
|
"bpc_rx_zero_pause_6",
|
|
"bpc_rx_zero_pause_7",
|
|
"bpc_rx_first_pause_0",
|
|
"bpc_rx_first_pause_1",
|
|
"bpc_rx_first_pause_2",
|
|
"bpc_rx_first_pause_3",
|
|
"bpc_rx_first_pause_4",
|
|
"bpc_rx_first_pause_5",
|
|
"bpc_rx_first_pause_6",
|
|
"bpc_rx_first_pause_7",
|
|
|
|
"rad_rx_frames",
|
|
"rad_rx_octets",
|
|
"rad_rx_vlan_frames",
|
|
"rad_rx_ucast",
|
|
"rad_rx_ucast_octets",
|
|
"rad_rx_ucast_vlan",
|
|
"rad_rx_mcast",
|
|
"rad_rx_mcast_octets",
|
|
"rad_rx_mcast_vlan",
|
|
"rad_rx_bcast",
|
|
"rad_rx_bcast_octets",
|
|
"rad_rx_bcast_vlan",
|
|
"rad_rx_drops",
|
|
|
|
"rlb_rad_rx_frames",
|
|
"rlb_rad_rx_octets",
|
|
"rlb_rad_rx_vlan_frames",
|
|
"rlb_rad_rx_ucast",
|
|
"rlb_rad_rx_ucast_octets",
|
|
"rlb_rad_rx_ucast_vlan",
|
|
"rlb_rad_rx_mcast",
|
|
"rlb_rad_rx_mcast_octets",
|
|
"rlb_rad_rx_mcast_vlan",
|
|
"rlb_rad_rx_bcast",
|
|
"rlb_rad_rx_bcast_octets",
|
|
"rlb_rad_rx_bcast_vlan",
|
|
"rlb_rad_rx_drops",
|
|
|
|
"fc_rx_ucast_octets",
|
|
"fc_rx_ucast",
|
|
"fc_rx_ucast_vlan",
|
|
"fc_rx_mcast_octets",
|
|
"fc_rx_mcast",
|
|
"fc_rx_mcast_vlan",
|
|
"fc_rx_bcast_octets",
|
|
"fc_rx_bcast",
|
|
"fc_rx_bcast_vlan",
|
|
|
|
"fc_tx_ucast_octets",
|
|
"fc_tx_ucast",
|
|
"fc_tx_ucast_vlan",
|
|
"fc_tx_mcast_octets",
|
|
"fc_tx_mcast",
|
|
"fc_tx_mcast_vlan",
|
|
"fc_tx_bcast_octets",
|
|
"fc_tx_bcast",
|
|
"fc_tx_bcast_vlan",
|
|
"fc_tx_parity_errors",
|
|
"fc_tx_timeout",
|
|
"fc_tx_fid_parity_errors",
|
|
};
|
|
|
|
#define BNAD_ETHTOOL_STATS_NUM ARRAY_SIZE(bnad_net_stats_strings)
|
|
|
|
static int
|
|
bnad_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
|
|
{
|
|
cmd->supported = SUPPORTED_10000baseT_Full;
|
|
cmd->advertising = ADVERTISED_10000baseT_Full;
|
|
cmd->autoneg = AUTONEG_DISABLE;
|
|
cmd->supported |= SUPPORTED_FIBRE;
|
|
cmd->advertising |= ADVERTISED_FIBRE;
|
|
cmd->port = PORT_FIBRE;
|
|
cmd->phy_address = 0;
|
|
|
|
if (netif_carrier_ok(netdev)) {
|
|
ethtool_cmd_speed_set(cmd, SPEED_10000);
|
|
cmd->duplex = DUPLEX_FULL;
|
|
} else {
|
|
ethtool_cmd_speed_set(cmd, SPEED_UNKNOWN);
|
|
cmd->duplex = DUPLEX_UNKNOWN;
|
|
}
|
|
cmd->transceiver = XCVR_EXTERNAL;
|
|
cmd->maxtxpkt = 0;
|
|
cmd->maxrxpkt = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bnad_set_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
|
|
{
|
|
/* 10G full duplex setting supported only */
|
|
if (cmd->autoneg == AUTONEG_ENABLE)
|
|
return -EOPNOTSUPP; else {
|
|
if ((ethtool_cmd_speed(cmd) == SPEED_10000)
|
|
&& (cmd->duplex == DUPLEX_FULL))
|
|
return 0;
|
|
}
|
|
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static void
|
|
bnad_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo)
|
|
{
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
struct bfa_ioc_attr *ioc_attr;
|
|
unsigned long flags;
|
|
|
|
strlcpy(drvinfo->driver, BNAD_NAME, sizeof(drvinfo->driver));
|
|
strlcpy(drvinfo->version, BNAD_VERSION, sizeof(drvinfo->version));
|
|
|
|
ioc_attr = kzalloc(sizeof(*ioc_attr), GFP_KERNEL);
|
|
if (ioc_attr) {
|
|
spin_lock_irqsave(&bnad->bna_lock, flags);
|
|
bfa_nw_ioc_get_attr(&bnad->bna.ioceth.ioc, ioc_attr);
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
|
|
strlcpy(drvinfo->fw_version, ioc_attr->adapter_attr.fw_ver,
|
|
sizeof(drvinfo->fw_version));
|
|
kfree(ioc_attr);
|
|
}
|
|
|
|
strlcpy(drvinfo->bus_info, pci_name(bnad->pcidev),
|
|
sizeof(drvinfo->bus_info));
|
|
}
|
|
|
|
static void
|
|
bnad_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wolinfo)
|
|
{
|
|
wolinfo->supported = 0;
|
|
wolinfo->wolopts = 0;
|
|
}
|
|
|
|
static int
|
|
bnad_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce)
|
|
{
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
unsigned long flags;
|
|
|
|
/* Lock rqd. to access bnad->bna_lock */
|
|
spin_lock_irqsave(&bnad->bna_lock, flags);
|
|
coalesce->use_adaptive_rx_coalesce =
|
|
(bnad->cfg_flags & BNAD_CF_DIM_ENABLED) ? true : false;
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
|
|
coalesce->rx_coalesce_usecs = bnad->rx_coalescing_timeo *
|
|
BFI_COALESCING_TIMER_UNIT;
|
|
coalesce->tx_coalesce_usecs = bnad->tx_coalescing_timeo *
|
|
BFI_COALESCING_TIMER_UNIT;
|
|
coalesce->tx_max_coalesced_frames = BFI_TX_INTERPKT_COUNT;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bnad_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce)
|
|
{
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
unsigned long flags;
|
|
int to_del = 0;
|
|
|
|
if (coalesce->rx_coalesce_usecs == 0 ||
|
|
coalesce->rx_coalesce_usecs >
|
|
BFI_MAX_COALESCING_TIMEO * BFI_COALESCING_TIMER_UNIT)
|
|
return -EINVAL;
|
|
|
|
if (coalesce->tx_coalesce_usecs == 0 ||
|
|
coalesce->tx_coalesce_usecs >
|
|
BFI_MAX_COALESCING_TIMEO * BFI_COALESCING_TIMER_UNIT)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&bnad->conf_mutex);
|
|
/*
|
|
* Do not need to store rx_coalesce_usecs here
|
|
* Every time DIM is disabled, we can get it from the
|
|
* stack.
|
|
*/
|
|
spin_lock_irqsave(&bnad->bna_lock, flags);
|
|
if (coalesce->use_adaptive_rx_coalesce) {
|
|
if (!(bnad->cfg_flags & BNAD_CF_DIM_ENABLED)) {
|
|
bnad->cfg_flags |= BNAD_CF_DIM_ENABLED;
|
|
bnad_dim_timer_start(bnad);
|
|
}
|
|
} else {
|
|
if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED) {
|
|
bnad->cfg_flags &= ~BNAD_CF_DIM_ENABLED;
|
|
if (bnad->cfg_flags & BNAD_CF_DIM_ENABLED &&
|
|
test_bit(BNAD_RF_DIM_TIMER_RUNNING,
|
|
&bnad->run_flags)) {
|
|
clear_bit(BNAD_RF_DIM_TIMER_RUNNING,
|
|
&bnad->run_flags);
|
|
to_del = 1;
|
|
}
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
if (to_del)
|
|
del_timer_sync(&bnad->dim_timer);
|
|
spin_lock_irqsave(&bnad->bna_lock, flags);
|
|
bnad_rx_coalescing_timeo_set(bnad);
|
|
}
|
|
}
|
|
if (bnad->tx_coalescing_timeo != coalesce->tx_coalesce_usecs /
|
|
BFI_COALESCING_TIMER_UNIT) {
|
|
bnad->tx_coalescing_timeo = coalesce->tx_coalesce_usecs /
|
|
BFI_COALESCING_TIMER_UNIT;
|
|
bnad_tx_coalescing_timeo_set(bnad);
|
|
}
|
|
|
|
if (bnad->rx_coalescing_timeo != coalesce->rx_coalesce_usecs /
|
|
BFI_COALESCING_TIMER_UNIT) {
|
|
bnad->rx_coalescing_timeo = coalesce->rx_coalesce_usecs /
|
|
BFI_COALESCING_TIMER_UNIT;
|
|
|
|
if (!(bnad->cfg_flags & BNAD_CF_DIM_ENABLED))
|
|
bnad_rx_coalescing_timeo_set(bnad);
|
|
|
|
}
|
|
|
|
/* Add Tx Inter-pkt DMA count? */
|
|
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
|
|
mutex_unlock(&bnad->conf_mutex);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
bnad_get_ringparam(struct net_device *netdev,
|
|
struct ethtool_ringparam *ringparam)
|
|
{
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
|
|
ringparam->rx_max_pending = BNAD_MAX_RXQ_DEPTH;
|
|
ringparam->tx_max_pending = BNAD_MAX_TXQ_DEPTH;
|
|
|
|
ringparam->rx_pending = bnad->rxq_depth;
|
|
ringparam->tx_pending = bnad->txq_depth;
|
|
}
|
|
|
|
static int
|
|
bnad_set_ringparam(struct net_device *netdev,
|
|
struct ethtool_ringparam *ringparam)
|
|
{
|
|
int i, current_err, err = 0;
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
unsigned long flags;
|
|
|
|
mutex_lock(&bnad->conf_mutex);
|
|
if (ringparam->rx_pending == bnad->rxq_depth &&
|
|
ringparam->tx_pending == bnad->txq_depth) {
|
|
mutex_unlock(&bnad->conf_mutex);
|
|
return 0;
|
|
}
|
|
|
|
if (ringparam->rx_pending < BNAD_MIN_Q_DEPTH ||
|
|
ringparam->rx_pending > BNAD_MAX_RXQ_DEPTH ||
|
|
!is_power_of_2(ringparam->rx_pending)) {
|
|
mutex_unlock(&bnad->conf_mutex);
|
|
return -EINVAL;
|
|
}
|
|
if (ringparam->tx_pending < BNAD_MIN_Q_DEPTH ||
|
|
ringparam->tx_pending > BNAD_MAX_TXQ_DEPTH ||
|
|
!is_power_of_2(ringparam->tx_pending)) {
|
|
mutex_unlock(&bnad->conf_mutex);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ringparam->rx_pending != bnad->rxq_depth) {
|
|
bnad->rxq_depth = ringparam->rx_pending;
|
|
if (!netif_running(netdev)) {
|
|
mutex_unlock(&bnad->conf_mutex);
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < bnad->num_rx; i++) {
|
|
if (!bnad->rx_info[i].rx)
|
|
continue;
|
|
bnad_destroy_rx(bnad, i);
|
|
current_err = bnad_setup_rx(bnad, i);
|
|
if (current_err && !err)
|
|
err = current_err;
|
|
}
|
|
|
|
if (!err && bnad->rx_info[0].rx) {
|
|
/* restore rx configuration */
|
|
bnad_restore_vlans(bnad, 0);
|
|
bnad_enable_default_bcast(bnad);
|
|
spin_lock_irqsave(&bnad->bna_lock, flags);
|
|
bnad_mac_addr_set_locked(bnad, netdev->dev_addr);
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
bnad->cfg_flags &= ~(BNAD_CF_ALLMULTI |
|
|
BNAD_CF_PROMISC);
|
|
bnad_set_rx_mode(netdev);
|
|
}
|
|
}
|
|
if (ringparam->tx_pending != bnad->txq_depth) {
|
|
bnad->txq_depth = ringparam->tx_pending;
|
|
if (!netif_running(netdev)) {
|
|
mutex_unlock(&bnad->conf_mutex);
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < bnad->num_tx; i++) {
|
|
if (!bnad->tx_info[i].tx)
|
|
continue;
|
|
bnad_destroy_tx(bnad, i);
|
|
current_err = bnad_setup_tx(bnad, i);
|
|
if (current_err && !err)
|
|
err = current_err;
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&bnad->conf_mutex);
|
|
return err;
|
|
}
|
|
|
|
static void
|
|
bnad_get_pauseparam(struct net_device *netdev,
|
|
struct ethtool_pauseparam *pauseparam)
|
|
{
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
|
|
pauseparam->autoneg = 0;
|
|
pauseparam->rx_pause = bnad->bna.enet.pause_config.rx_pause;
|
|
pauseparam->tx_pause = bnad->bna.enet.pause_config.tx_pause;
|
|
}
|
|
|
|
static int
|
|
bnad_set_pauseparam(struct net_device *netdev,
|
|
struct ethtool_pauseparam *pauseparam)
|
|
{
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
struct bna_pause_config pause_config;
|
|
unsigned long flags;
|
|
|
|
if (pauseparam->autoneg == AUTONEG_ENABLE)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&bnad->conf_mutex);
|
|
if (pauseparam->rx_pause != bnad->bna.enet.pause_config.rx_pause ||
|
|
pauseparam->tx_pause != bnad->bna.enet.pause_config.tx_pause) {
|
|
pause_config.rx_pause = pauseparam->rx_pause;
|
|
pause_config.tx_pause = pauseparam->tx_pause;
|
|
spin_lock_irqsave(&bnad->bna_lock, flags);
|
|
bna_enet_pause_config(&bnad->bna.enet, &pause_config);
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
}
|
|
mutex_unlock(&bnad->conf_mutex);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
bnad_get_strings(struct net_device *netdev, u32 stringset, u8 *string)
|
|
{
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
int i, j, q_num;
|
|
u32 bmap;
|
|
|
|
mutex_lock(&bnad->conf_mutex);
|
|
|
|
switch (stringset) {
|
|
case ETH_SS_STATS:
|
|
for (i = 0; i < BNAD_ETHTOOL_STATS_NUM; i++) {
|
|
BUG_ON(!(strlen(bnad_net_stats_strings[i]) <
|
|
ETH_GSTRING_LEN));
|
|
memcpy(string, bnad_net_stats_strings[i],
|
|
ETH_GSTRING_LEN);
|
|
string += ETH_GSTRING_LEN;
|
|
}
|
|
bmap = bna_tx_rid_mask(&bnad->bna);
|
|
for (i = 0; bmap; i++) {
|
|
if (bmap & 1) {
|
|
sprintf(string, "txf%d_ucast_octets", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txf%d_ucast", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txf%d_ucast_vlan", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txf%d_mcast_octets", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txf%d_mcast", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txf%d_mcast_vlan", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txf%d_bcast_octets", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txf%d_bcast", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txf%d_bcast_vlan", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txf%d_errors", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txf%d_filter_vlan", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txf%d_filter_mac_sa", i);
|
|
string += ETH_GSTRING_LEN;
|
|
}
|
|
bmap >>= 1;
|
|
}
|
|
|
|
bmap = bna_rx_rid_mask(&bnad->bna);
|
|
for (i = 0; bmap; i++) {
|
|
if (bmap & 1) {
|
|
sprintf(string, "rxf%d_ucast_octets", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxf%d_ucast", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxf%d_ucast_vlan", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxf%d_mcast_octets", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxf%d_mcast", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxf%d_mcast_vlan", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxf%d_bcast_octets", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxf%d_bcast", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxf%d_bcast_vlan", i);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxf%d_frame_drops", i);
|
|
string += ETH_GSTRING_LEN;
|
|
}
|
|
bmap >>= 1;
|
|
}
|
|
|
|
q_num = 0;
|
|
for (i = 0; i < bnad->num_rx; i++) {
|
|
if (!bnad->rx_info[i].rx)
|
|
continue;
|
|
for (j = 0; j < bnad->num_rxp_per_rx; j++) {
|
|
sprintf(string, "cq%d_producer_index", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "cq%d_consumer_index", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "cq%d_hw_producer_index",
|
|
q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "cq%d_intr", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "cq%d_poll", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "cq%d_schedule", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "cq%d_keep_poll", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "cq%d_complete", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
q_num++;
|
|
}
|
|
}
|
|
|
|
q_num = 0;
|
|
for (i = 0; i < bnad->num_rx; i++) {
|
|
if (!bnad->rx_info[i].rx)
|
|
continue;
|
|
for (j = 0; j < bnad->num_rxp_per_rx; j++) {
|
|
sprintf(string, "rxq%d_packets", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxq%d_bytes", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxq%d_packets_with_error",
|
|
q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxq%d_allocbuf_failed", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxq%d_mapbuf_failed", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxq%d_producer_index", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxq%d_consumer_index", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
q_num++;
|
|
if (bnad->rx_info[i].rx_ctrl[j].ccb &&
|
|
bnad->rx_info[i].rx_ctrl[j].ccb->
|
|
rcb[1] &&
|
|
bnad->rx_info[i].rx_ctrl[j].ccb->
|
|
rcb[1]->rxq) {
|
|
sprintf(string, "rxq%d_packets", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxq%d_bytes", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string,
|
|
"rxq%d_packets_with_error", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxq%d_allocbuf_failed",
|
|
q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxq%d_mapbuf_failed",
|
|
q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxq%d_producer_index",
|
|
q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "rxq%d_consumer_index",
|
|
q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
q_num++;
|
|
}
|
|
}
|
|
}
|
|
|
|
q_num = 0;
|
|
for (i = 0; i < bnad->num_tx; i++) {
|
|
if (!bnad->tx_info[i].tx)
|
|
continue;
|
|
for (j = 0; j < bnad->num_txq_per_tx; j++) {
|
|
sprintf(string, "txq%d_packets", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txq%d_bytes", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txq%d_producer_index", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txq%d_consumer_index", q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
sprintf(string, "txq%d_hw_consumer_index",
|
|
q_num);
|
|
string += ETH_GSTRING_LEN;
|
|
q_num++;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
mutex_unlock(&bnad->conf_mutex);
|
|
}
|
|
|
|
static int
|
|
bnad_get_stats_count_locked(struct net_device *netdev)
|
|
{
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
int i, j, count = 0, rxf_active_num = 0, txf_active_num = 0;
|
|
u32 bmap;
|
|
|
|
bmap = bna_tx_rid_mask(&bnad->bna);
|
|
for (i = 0; bmap; i++) {
|
|
if (bmap & 1)
|
|
txf_active_num++;
|
|
bmap >>= 1;
|
|
}
|
|
bmap = bna_rx_rid_mask(&bnad->bna);
|
|
for (i = 0; bmap; i++) {
|
|
if (bmap & 1)
|
|
rxf_active_num++;
|
|
bmap >>= 1;
|
|
}
|
|
count = BNAD_ETHTOOL_STATS_NUM +
|
|
txf_active_num * BNAD_NUM_TXF_COUNTERS +
|
|
rxf_active_num * BNAD_NUM_RXF_COUNTERS;
|
|
|
|
for (i = 0; i < bnad->num_rx; i++) {
|
|
if (!bnad->rx_info[i].rx)
|
|
continue;
|
|
count += bnad->num_rxp_per_rx * BNAD_NUM_CQ_COUNTERS;
|
|
count += bnad->num_rxp_per_rx * BNAD_NUM_RXQ_COUNTERS;
|
|
for (j = 0; j < bnad->num_rxp_per_rx; j++)
|
|
if (bnad->rx_info[i].rx_ctrl[j].ccb &&
|
|
bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1] &&
|
|
bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1]->rxq)
|
|
count += BNAD_NUM_RXQ_COUNTERS;
|
|
}
|
|
|
|
for (i = 0; i < bnad->num_tx; i++) {
|
|
if (!bnad->tx_info[i].tx)
|
|
continue;
|
|
count += bnad->num_txq_per_tx * BNAD_NUM_TXQ_COUNTERS;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int
|
|
bnad_per_q_stats_fill(struct bnad *bnad, u64 *buf, int bi)
|
|
{
|
|
int i, j;
|
|
struct bna_rcb *rcb = NULL;
|
|
struct bna_tcb *tcb = NULL;
|
|
|
|
for (i = 0; i < bnad->num_rx; i++) {
|
|
if (!bnad->rx_info[i].rx)
|
|
continue;
|
|
for (j = 0; j < bnad->num_rxp_per_rx; j++)
|
|
if (bnad->rx_info[i].rx_ctrl[j].ccb &&
|
|
bnad->rx_info[i].rx_ctrl[j].ccb->rcb[0] &&
|
|
bnad->rx_info[i].rx_ctrl[j].ccb->rcb[0]->rxq) {
|
|
buf[bi++] = bnad->rx_info[i].rx_ctrl[j].
|
|
ccb->producer_index;
|
|
buf[bi++] = 0; /* ccb->consumer_index */
|
|
buf[bi++] = *(bnad->rx_info[i].rx_ctrl[j].
|
|
ccb->hw_producer_index);
|
|
|
|
buf[bi++] = bnad->rx_info[i].
|
|
rx_ctrl[j].rx_intr_ctr;
|
|
buf[bi++] = bnad->rx_info[i].
|
|
rx_ctrl[j].rx_poll_ctr;
|
|
buf[bi++] = bnad->rx_info[i].
|
|
rx_ctrl[j].rx_schedule;
|
|
buf[bi++] = bnad->rx_info[i].
|
|
rx_ctrl[j].rx_keep_poll;
|
|
buf[bi++] = bnad->rx_info[i].
|
|
rx_ctrl[j].rx_complete;
|
|
}
|
|
}
|
|
for (i = 0; i < bnad->num_rx; i++) {
|
|
if (!bnad->rx_info[i].rx)
|
|
continue;
|
|
for (j = 0; j < bnad->num_rxp_per_rx; j++)
|
|
if (bnad->rx_info[i].rx_ctrl[j].ccb) {
|
|
if (bnad->rx_info[i].rx_ctrl[j].ccb->rcb[0] &&
|
|
bnad->rx_info[i].rx_ctrl[j].ccb->
|
|
rcb[0]->rxq) {
|
|
rcb = bnad->rx_info[i].rx_ctrl[j].
|
|
ccb->rcb[0];
|
|
buf[bi++] = rcb->rxq->rx_packets;
|
|
buf[bi++] = rcb->rxq->rx_bytes;
|
|
buf[bi++] = rcb->rxq->
|
|
rx_packets_with_error;
|
|
buf[bi++] = rcb->rxq->
|
|
rxbuf_alloc_failed;
|
|
buf[bi++] = rcb->rxq->rxbuf_map_failed;
|
|
buf[bi++] = rcb->producer_index;
|
|
buf[bi++] = rcb->consumer_index;
|
|
}
|
|
if (bnad->rx_info[i].rx_ctrl[j].ccb->rcb[1] &&
|
|
bnad->rx_info[i].rx_ctrl[j].ccb->
|
|
rcb[1]->rxq) {
|
|
rcb = bnad->rx_info[i].rx_ctrl[j].
|
|
ccb->rcb[1];
|
|
buf[bi++] = rcb->rxq->rx_packets;
|
|
buf[bi++] = rcb->rxq->rx_bytes;
|
|
buf[bi++] = rcb->rxq->
|
|
rx_packets_with_error;
|
|
buf[bi++] = rcb->rxq->
|
|
rxbuf_alloc_failed;
|
|
buf[bi++] = rcb->rxq->rxbuf_map_failed;
|
|
buf[bi++] = rcb->producer_index;
|
|
buf[bi++] = rcb->consumer_index;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < bnad->num_tx; i++) {
|
|
if (!bnad->tx_info[i].tx)
|
|
continue;
|
|
for (j = 0; j < bnad->num_txq_per_tx; j++)
|
|
if (bnad->tx_info[i].tcb[j] &&
|
|
bnad->tx_info[i].tcb[j]->txq) {
|
|
tcb = bnad->tx_info[i].tcb[j];
|
|
buf[bi++] = tcb->txq->tx_packets;
|
|
buf[bi++] = tcb->txq->tx_bytes;
|
|
buf[bi++] = tcb->producer_index;
|
|
buf[bi++] = tcb->consumer_index;
|
|
buf[bi++] = *(tcb->hw_consumer_index);
|
|
}
|
|
}
|
|
|
|
return bi;
|
|
}
|
|
|
|
static void
|
|
bnad_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats,
|
|
u64 *buf)
|
|
{
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
int i, j, bi = 0;
|
|
unsigned long flags;
|
|
struct rtnl_link_stats64 net_stats64;
|
|
u64 *stats64;
|
|
u32 bmap;
|
|
|
|
mutex_lock(&bnad->conf_mutex);
|
|
if (bnad_get_stats_count_locked(netdev) != stats->n_stats) {
|
|
mutex_unlock(&bnad->conf_mutex);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Used bna_lock to sync reads from bna_stats, which is written
|
|
* under the same lock
|
|
*/
|
|
spin_lock_irqsave(&bnad->bna_lock, flags);
|
|
|
|
memset(&net_stats64, 0, sizeof(net_stats64));
|
|
bnad_netdev_qstats_fill(bnad, &net_stats64);
|
|
bnad_netdev_hwstats_fill(bnad, &net_stats64);
|
|
|
|
buf[bi++] = net_stats64.rx_packets;
|
|
buf[bi++] = net_stats64.tx_packets;
|
|
buf[bi++] = net_stats64.rx_bytes;
|
|
buf[bi++] = net_stats64.tx_bytes;
|
|
buf[bi++] = net_stats64.rx_errors;
|
|
buf[bi++] = net_stats64.tx_errors;
|
|
buf[bi++] = net_stats64.rx_dropped;
|
|
buf[bi++] = net_stats64.tx_dropped;
|
|
buf[bi++] = net_stats64.multicast;
|
|
buf[bi++] = net_stats64.collisions;
|
|
buf[bi++] = net_stats64.rx_length_errors;
|
|
buf[bi++] = net_stats64.rx_crc_errors;
|
|
buf[bi++] = net_stats64.rx_frame_errors;
|
|
buf[bi++] = net_stats64.tx_fifo_errors;
|
|
|
|
/* Get netif_queue_stopped from stack */
|
|
bnad->stats.drv_stats.netif_queue_stopped = netif_queue_stopped(netdev);
|
|
|
|
/* Fill driver stats into ethtool buffers */
|
|
stats64 = (u64 *)&bnad->stats.drv_stats;
|
|
for (i = 0; i < sizeof(struct bnad_drv_stats) / sizeof(u64); i++)
|
|
buf[bi++] = stats64[i];
|
|
|
|
/* Fill hardware stats excluding the rxf/txf into ethtool bufs */
|
|
stats64 = (u64 *) &bnad->stats.bna_stats->hw_stats;
|
|
for (i = 0;
|
|
i < offsetof(struct bfi_enet_stats, rxf_stats[0]) /
|
|
sizeof(u64);
|
|
i++)
|
|
buf[bi++] = stats64[i];
|
|
|
|
/* Fill txf stats into ethtool buffers */
|
|
bmap = bna_tx_rid_mask(&bnad->bna);
|
|
for (i = 0; bmap; i++) {
|
|
if (bmap & 1) {
|
|
stats64 = (u64 *)&bnad->stats.bna_stats->
|
|
hw_stats.txf_stats[i];
|
|
for (j = 0; j < sizeof(struct bfi_enet_stats_txf) /
|
|
sizeof(u64); j++)
|
|
buf[bi++] = stats64[j];
|
|
}
|
|
bmap >>= 1;
|
|
}
|
|
|
|
/* Fill rxf stats into ethtool buffers */
|
|
bmap = bna_rx_rid_mask(&bnad->bna);
|
|
for (i = 0; bmap; i++) {
|
|
if (bmap & 1) {
|
|
stats64 = (u64 *)&bnad->stats.bna_stats->
|
|
hw_stats.rxf_stats[i];
|
|
for (j = 0; j < sizeof(struct bfi_enet_stats_rxf) /
|
|
sizeof(u64); j++)
|
|
buf[bi++] = stats64[j];
|
|
}
|
|
bmap >>= 1;
|
|
}
|
|
|
|
/* Fill per Q stats into ethtool buffers */
|
|
bi = bnad_per_q_stats_fill(bnad, buf, bi);
|
|
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
|
|
mutex_unlock(&bnad->conf_mutex);
|
|
}
|
|
|
|
static int
|
|
bnad_get_sset_count(struct net_device *netdev, int sset)
|
|
{
|
|
switch (sset) {
|
|
case ETH_SS_STATS:
|
|
return bnad_get_stats_count_locked(netdev);
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
}
|
|
|
|
static u32
|
|
bnad_get_flash_partition_by_offset(struct bnad *bnad, u32 offset,
|
|
u32 *base_offset)
|
|
{
|
|
struct bfa_flash_attr *flash_attr;
|
|
struct bnad_iocmd_comp fcomp;
|
|
u32 i, flash_part = 0, ret;
|
|
unsigned long flags = 0;
|
|
|
|
flash_attr = kzalloc(sizeof(struct bfa_flash_attr), GFP_KERNEL);
|
|
if (!flash_attr)
|
|
return 0;
|
|
|
|
fcomp.bnad = bnad;
|
|
fcomp.comp_status = 0;
|
|
|
|
init_completion(&fcomp.comp);
|
|
spin_lock_irqsave(&bnad->bna_lock, flags);
|
|
ret = bfa_nw_flash_get_attr(&bnad->bna.flash, flash_attr,
|
|
bnad_cb_completion, &fcomp);
|
|
if (ret != BFA_STATUS_OK) {
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
kfree(flash_attr);
|
|
return 0;
|
|
}
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
wait_for_completion(&fcomp.comp);
|
|
ret = fcomp.comp_status;
|
|
|
|
/* Check for the flash type & base offset value */
|
|
if (ret == BFA_STATUS_OK) {
|
|
for (i = 0; i < flash_attr->npart; i++) {
|
|
if (offset >= flash_attr->part[i].part_off &&
|
|
offset < (flash_attr->part[i].part_off +
|
|
flash_attr->part[i].part_size)) {
|
|
flash_part = flash_attr->part[i].part_type;
|
|
*base_offset = flash_attr->part[i].part_off;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
kfree(flash_attr);
|
|
return flash_part;
|
|
}
|
|
|
|
static int
|
|
bnad_get_eeprom_len(struct net_device *netdev)
|
|
{
|
|
return BFA_TOTAL_FLASH_SIZE;
|
|
}
|
|
|
|
static int
|
|
bnad_get_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
|
|
u8 *bytes)
|
|
{
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
struct bnad_iocmd_comp fcomp;
|
|
u32 flash_part = 0, base_offset = 0;
|
|
unsigned long flags = 0;
|
|
int ret = 0;
|
|
|
|
/* Fill the magic value */
|
|
eeprom->magic = bnad->pcidev->vendor | (bnad->pcidev->device << 16);
|
|
|
|
/* Query the flash partition based on the offset */
|
|
flash_part = bnad_get_flash_partition_by_offset(bnad,
|
|
eeprom->offset, &base_offset);
|
|
if (flash_part == 0)
|
|
return -EFAULT;
|
|
|
|
fcomp.bnad = bnad;
|
|
fcomp.comp_status = 0;
|
|
|
|
init_completion(&fcomp.comp);
|
|
spin_lock_irqsave(&bnad->bna_lock, flags);
|
|
ret = bfa_nw_flash_read_part(&bnad->bna.flash, flash_part,
|
|
bnad->id, bytes, eeprom->len,
|
|
eeprom->offset - base_offset,
|
|
bnad_cb_completion, &fcomp);
|
|
if (ret != BFA_STATUS_OK) {
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
goto done;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
wait_for_completion(&fcomp.comp);
|
|
ret = fcomp.comp_status;
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
bnad_set_eeprom(struct net_device *netdev, struct ethtool_eeprom *eeprom,
|
|
u8 *bytes)
|
|
{
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
struct bnad_iocmd_comp fcomp;
|
|
u32 flash_part = 0, base_offset = 0;
|
|
unsigned long flags = 0;
|
|
int ret = 0;
|
|
|
|
/* Check if the flash update request is valid */
|
|
if (eeprom->magic != (bnad->pcidev->vendor |
|
|
(bnad->pcidev->device << 16)))
|
|
return -EINVAL;
|
|
|
|
/* Query the flash partition based on the offset */
|
|
flash_part = bnad_get_flash_partition_by_offset(bnad,
|
|
eeprom->offset, &base_offset);
|
|
if (flash_part == 0)
|
|
return -EFAULT;
|
|
|
|
fcomp.bnad = bnad;
|
|
fcomp.comp_status = 0;
|
|
|
|
init_completion(&fcomp.comp);
|
|
spin_lock_irqsave(&bnad->bna_lock, flags);
|
|
ret = bfa_nw_flash_update_part(&bnad->bna.flash, flash_part,
|
|
bnad->id, bytes, eeprom->len,
|
|
eeprom->offset - base_offset,
|
|
bnad_cb_completion, &fcomp);
|
|
if (ret != BFA_STATUS_OK) {
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
goto done;
|
|
}
|
|
|
|
spin_unlock_irqrestore(&bnad->bna_lock, flags);
|
|
wait_for_completion(&fcomp.comp);
|
|
ret = fcomp.comp_status;
|
|
done:
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
bnad_flash_device(struct net_device *netdev, struct ethtool_flash *eflash)
|
|
{
|
|
struct bnad *bnad = netdev_priv(netdev);
|
|
struct bnad_iocmd_comp fcomp;
|
|
const struct firmware *fw;
|
|
int ret = 0;
|
|
|
|
ret = request_firmware(&fw, eflash->data, &bnad->pcidev->dev);
|
|
if (ret) {
|
|
netdev_err(netdev, "can't load firmware %s\n", eflash->data);
|
|
goto out;
|
|
}
|
|
|
|
fcomp.bnad = bnad;
|
|
fcomp.comp_status = 0;
|
|
|
|
init_completion(&fcomp.comp);
|
|
spin_lock_irq(&bnad->bna_lock);
|
|
ret = bfa_nw_flash_update_part(&bnad->bna.flash, BFA_FLASH_PART_FWIMG,
|
|
bnad->id, (u8 *)fw->data, fw->size, 0,
|
|
bnad_cb_completion, &fcomp);
|
|
if (ret != BFA_STATUS_OK) {
|
|
netdev_warn(netdev, "flash update failed with err=%d\n", ret);
|
|
ret = -EIO;
|
|
spin_unlock_irq(&bnad->bna_lock);
|
|
goto out;
|
|
}
|
|
|
|
spin_unlock_irq(&bnad->bna_lock);
|
|
wait_for_completion(&fcomp.comp);
|
|
if (fcomp.comp_status != BFA_STATUS_OK) {
|
|
ret = -EIO;
|
|
netdev_warn(netdev,
|
|
"firmware image update failed with err=%d\n",
|
|
fcomp.comp_status);
|
|
}
|
|
out:
|
|
release_firmware(fw);
|
|
return ret;
|
|
}
|
|
|
|
static const struct ethtool_ops bnad_ethtool_ops = {
|
|
.get_settings = bnad_get_settings,
|
|
.set_settings = bnad_set_settings,
|
|
.get_drvinfo = bnad_get_drvinfo,
|
|
.get_wol = bnad_get_wol,
|
|
.get_link = ethtool_op_get_link,
|
|
.get_coalesce = bnad_get_coalesce,
|
|
.set_coalesce = bnad_set_coalesce,
|
|
.get_ringparam = bnad_get_ringparam,
|
|
.set_ringparam = bnad_set_ringparam,
|
|
.get_pauseparam = bnad_get_pauseparam,
|
|
.set_pauseparam = bnad_set_pauseparam,
|
|
.get_strings = bnad_get_strings,
|
|
.get_ethtool_stats = bnad_get_ethtool_stats,
|
|
.get_sset_count = bnad_get_sset_count,
|
|
.get_eeprom_len = bnad_get_eeprom_len,
|
|
.get_eeprom = bnad_get_eeprom,
|
|
.set_eeprom = bnad_set_eeprom,
|
|
.flash_device = bnad_flash_device,
|
|
.get_ts_info = ethtool_op_get_ts_info,
|
|
};
|
|
|
|
void
|
|
bnad_set_ethtool_ops(struct net_device *netdev)
|
|
{
|
|
netdev->ethtool_ops = &bnad_ethtool_ops;
|
|
}
|